﻿'Description:装试（总体）排产后台方法
'Copyright (c) : 通力凯顿（北京）系统集成有限公司
'Writer:yuxj,longbin
'create Date:20220829
'Rewriter:
'Rewrite Date:
Imports System.Text
Imports BusinessRule.DTO
Imports DatabaseAccess.DBUtility
Imports DatabaseAccess.IDAL
Imports DatabaseAccess.DALFactory
Imports System.Data.OracleClient
Public Class ZSLimitedCapacityBusiness 
    Dim m_fullab As FullStatisticsAnalysisBusiness = New FullStatisticsAnalysisBusiness()
    Dim faMakeCalendarDt As New DataTable '总体分厂日历
    Dim containersApsId As String = "" '本次排产的批次组共用的ApsId

#Region "装试排产--查询方法"
    ''' <summary>
    ''' 装试排产--查询--排产池任务单
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetZsApsOrderTask(ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim strSql = <string>SELECT w2.WORKFLOWSTEPNAME,e2.MFGORDERID,c.MFGORDERNAME ,UA1.ATTRIBUTEVALUE ENGINENO,e2.CONTAINERNO,c.CONTAINERID,
                            c.PRODUCTDESCRIPTION,c.PRODUCTNAME,c.SPECNAME,wfb.workflowname||':'||wf.WorkflowRevision WFR,wf.description wfdesc,e2.workflowid,
                            to_char(c.expectmaterialdate,'yyyy/mm/dd') expectmaterialdate,
                            e2.plannedstartdate, e2.plannedfinishdate,decode(WF.WORKFLOWTYPE,'施工工艺','临时','技通工艺','临时','正常') WORKFLOWTYPE,
                            p.DESCRIPTION PRIO
                            FROM ENGINEPARTPLAN e2
                            LEFT JOIN CONTAINERCURRENTINFO c ON c.CONTAINERID=nvl(e2.CONTAINERID,e2.ENGINECONTAINERID) 
                            LEFT JOIN WORKFLOWSTEP w2 ON w2.WORKFLOWSTEPID =e2.WORKFLOWSTEPID 
                            LEFT JOIN USERATTRIBUTE UA1 ON UA1.PARENTID = C.MFGORDERID AND UA1.USERATTRIBUTENAME = '发动机'
                            LEFT JOIN workflow wf ON wf.workflowid=e2.workflowid
                            left join workflowbase wfb on wfb.workflowbaseid=wf.workflowbaseid
                            left join bom b on b.workflowid=wf.workflowid
                            left join billtype bt on bt.billtypeid=b.billtypeid --and bt.billtypename='大小件目录'
                            LEFT JOIN MFGORDER m ON m.MFGORDERID =e2.MFGORDERID 
                            LEFT JOIN PRIORITYCODE p  ON p.PRIORITYCODEID =m.PRIORITYID 
                            WHERE 1=1 AND nvl(e2.Usecompleted,0)=0
                    </string>.Value

        If para.ContainsKey("ContainerNo") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerNo")) Then
                strSql += String.Format(" and e2.ContainerNo like '%{0}%' ", para("ContainerNo"))
            End If
        End If

        If para.ContainsKey("Machine") Then
            If Not String.IsNullOrWhiteSpace(para("Machine")) Then
                strSql += String.Format(" and UA1.attributevalue like '%{0}%' ", para("Machine"))
            End If
        End If

        If para.ContainsKey("MfgOrdername") Then
            If Not String.IsNullOrWhiteSpace(para("MfgOrdername")) Then
                strSql += String.Format(" and m.mfgordername like '%{0}%' ", para("MfgOrdername"))
            End If
        End If

        If para.ContainsKey("WorkFlowType") Then
            If Not String.IsNullOrWhiteSpace(para("WorkFlowType")) Then
                strSql += String.Format(" and decode(WF.WORKFLOWTYPE,'施工工艺','0','技通工艺','0','1')='{0}' ", para("WorkFlowType"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and c.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("HasBom") Then
            If Not String.IsNullOrWhiteSpace(para("HasBom")) Then
                'strSql += String.Format(" and DECODE(c.BOMID,NULL,'0','1')='{0}' ", para("HasBom"))
                If para("HasBom") = "1" Then
                    strSql += "and bt.billtypename='大小件目录'" '"and bt.billtypeid is not null "
                Else
                    strSql += "and e2.bigsmallbomid is null" '"and bt.billtypeid is null "
                End If
            End If
        End If
        If para.ContainsKey("IsPool") Then
            If Not String.IsNullOrWhiteSpace(para("IsPool")) Then
                strSql += String.Format(" and nvl(c.ISINPOOL,'0')='{0}' ", para("IsPool"))
            End If
        End If

        If para.ContainsKey("PlanStartTime") Then
            If Not String.IsNullOrWhiteSpace(para("PlanStartTime")) Then
                strSql += String.Format(" and e2.plannedstartdate>=to_date('{0}','yyyy/mm/dd') ", para("PlanStartTime"))
            End If
        End If

        If para.ContainsKey("PlanEndTime") Then
            If Not String.IsNullOrWhiteSpace(para("PlanEndTime")) Then
                strSql += String.Format(" and e2.plannedfinishdate <=to_date('{0}','yyyy/mm/dd')+1 ", para("PlanEndTime"))
            End If
        End If
        '计划完成时间在前的优先排产,订单优先级最高
        strSql += " ORDER BY e2.plannedfinishdate asc,p.prioritycodename asc, w2.WORKFLOWSTEPNAME,c.CONTAINERNO"
        Dim paraPage As New uMESPagingDataDTO
        paraPage.CurrentPageIndex = Convert.ToInt32(para("CurrentPageIndex"))
        paraPage.PageSize = Convert.ToInt32(para("PageSize").ToString)
        paraPage.strSQL = strSql
        Dim result As uMESProcessResult = DatabaseHelper.RunProcedureForPage(paraPage)
        Return result
    End Function

    ''' <summary>
    ''' 根据订单号--查询--已排产的任务单
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetZsApsPlanInfoBymfgOrder(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim Strsql As String = <string>
                            SELECT a.APSPLANINFOID,c.CONTAINERNO||'('||w2.WORKFLOWSTEPNAME||')'||'-'||CASE WHEN instr(a.APSTYPE,'5')>0 THEN '正排' ELSE '倒排' END  CONTAINERNO 
                            FROM APSPLANINFO a 
                            LEFT JOIN CONTAINERCURRENTINFO c ON c.CONTAINERID =a.CONTAINERID 
                            LEFT JOIN ENGINEPARTPLAN e ON e.CONTAINERID =a.CONTAINERID 
                            LEFT JOIN WORKFLOWSTEP w2 ON w2.WORKFLOWSTEPID =e.WORKFLOWSTEPID 
                            WHERE 1=1
                               </string>
        If para.ContainsKey("MfgOrdername") Then
            If Not String.IsNullOrWhiteSpace(para("MfgOrdername")) Then
                Strsql += String.Format(" and c.mfgordername like '%{0}%' ", para("MfgOrdername"))
            End If
        End If
        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                Strsql += String.Format(" and c.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If
        If para.ContainsKey("status") Then
            If Not String.IsNullOrWhiteSpace(para("status")) Then
                Strsql += String.Format(" and nvl(a.status,0)='{0}' ", para("status"))
            End If
        End If
        Return DatabaseHelper.Query_ReturnDatTable(Strsql)
    End Function

    ''' <summary>
    ''' 获取工序级的排产信息
    ''' 增加颜色判断栏位yuxj20230419
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetStepInfiniteAps(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string> 
              select 1 qty,
                       ai.ApsType,
                       ai.containerid,
                       ai.apsplaninfoid,
                       ep.CONTAINERNO     containername,
                       ai.starttime        constarttime,
                       ai.endtime          conendtime,
                       ai.workflowid,
                       ad.apsplandetailid,
                       ad.workflowstepid,
                       ad.workflowstepname,
                       ad.starttime        stepstarttime,
                       ad.endtime          stependtime,ws.workstationname,w.workflowtype,
                        case when ad.ResStartTime {0} trunc(ep.plannedstartdate) and ad.resstarttime{1} trunc(sysdate) then '1' --红色
                            when ad.ResStartTime {2} trunc(ep.plannedstartdate) and ad.resstarttime {3} sysdate then '2'--黄色
                            when ad.resendtime {4} ep.plannedfinishdate then '1' --红色
                            else '0' end showcolor--正常   
                  from ApsPlanInfo ai
                  inner join enginepartplan ep on nvl(nvl(ep.CONTAINERID,ep.concontainerid),ep.ENGINECONTAINERID) = ai.containerid
                  left join ApsPlanDetail ad
                    on ad.apsplaninfoid = ai.apsplaninfoid
                  left join workstation ws
                    on ws.workstationid = ad.workstationid 
                  left join workflow w on w.workflowid =ai.workflowid 
                   where ad.isuse = 0  </string>.Value
        strSql = String.Format(strSql, "<", "<", "<", ">=", ">")
        If para.ContainsKey("ApsPlanDetailID") Then
            strSql += String.Format(" and ad.ApsPlanDetailID='{0}' ", para("ApsPlanDetailID"))
        End If
        If para.ContainsKey("ApsPlanInfoID") Then
            strSql += String.Format(" and ad.ApsPlanInfoID='{0}' ", para("ApsPlanInfoID"))
        End If

        If para.ContainsKey("ApsPlanInfoIDs") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanInfoIDs")) Then
                strSql += String.Format(" and ad.ApsPlanInfoID in ('{0}') ", para("ApsPlanInfoIDs").Replace(",", "','"))
            End If
        End If

        If para.ContainsKey("ContainerID") Then
            strSql += String.Format(" and ad.ContainerID IN('{0}') ", para("ContainerID").Replace(",", "','"))
        End If

        If para.ContainsKey("WorkflowStepID") Then
            strSql += String.Format(" and ad.WorkflowStepID='{0}' ", para("WorkflowStepID"))
        End If

        If para.ContainsKey("MfgorderName") Then
            strSql += String.Format(" and ep.mfgorderName like '%{0}%' ", para("MfgorderName"))
        End If

        If para.ContainsKey("ContainerNo") Then
            strSql += String.Format(" and ep.ContainerNo like '%{0}%' ", para("ContainerNo"))
        End If

        If para.ContainsKey("Machine") Then
            strSql += String.Format(" and ep.machineno like '%{0}%' ", para("Machine"))
        End If

        If para.ContainsKey("PlanStartTime") Then
            If Not String.IsNullOrWhiteSpace(para("PlanStartTime")) Then
                strSql += String.Format(" and ai.starttime>=to_date('{0}','yyyy/mm/dd') ", para("PlanStartTime")) 'modify by  longb 2023-10-12 ep.plannedstartdate
            End If
        End If

        If para.ContainsKey("PlanEndTime") Then
            If Not String.IsNullOrWhiteSpace(para("PlanEndTime")) Then
                strSql += String.Format(" and ai.endtime <=to_date('{0}','yyyy/mm/dd')+1 ", para("PlanEndTime")) 'modify by  longb 2023-10-12 ep.plannedfinishdate
            End If
        End If

        If para.ContainsKey("apsType") Then
            If Not String.IsNullOrWhiteSpace(para("apsType")) Then
                strSql += String.Format(" and ai.ApsType='{0}' ", para("apsType"))
            End If
        End If

        If para.ContainsKey("status") Then
            If Not String.IsNullOrWhiteSpace(para("status")) Then
                strSql += String.Format(" and nvl(ai.status,0)='{0}' ", para("status"))
                strSql += " AND NOT EXISTS (SELECT 1 FROM apsplaninfo WHERE status=1 AND containerid=ai.containerid)" 'add by longb 2023-11-16 新增过滤（同批次存在已下发的策略数据，不能再次调整，下发）
            End If
        End If

        strSql += " order by ai.starttime,ai.containerid,ai.workflowid,ad.workflowstepid "

        Return DatabaseHelper.Query_ReturnDatTable(strSql)

    End Function
    ''' <summary>
    ''' 排产计划确认
    ''' </summary>
    ''' <param name="dt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function UpdateApsPlanStatus(ByVal employeeId As String, ByVal dt As DataTable, ByVal status As String) As Boolean
        Dim sqlList As New ArrayList
        For Each dr As DataRow In dt.Rows
            sqlList.Add(String.Format("UPDATE APSPLANINFO SET STATUS='{0}' WHERE CONTAINERID='{1}' AND APSTYPE='{2}' and isuse=0", status, dr("CONTAINERID"), dr("APSTYPE")))
            sqlList.Add(String.Format("UPDATE apsplandetail ap SET ap.confirmempid = '{0}',ap.confirmdate =sysdate,ap.status = '{1}' where APSPLANINFOID=(select APSPLANINFOID from APSPLANINFO where CONTAINERID='{2}' AND APSTYPE='{3}'  and isuse=0)", employeeId, status, dr("CONTAINERID"), dr("APSTYPE")))
        Next
        Return DatabaseHelper.ExecuteSqlListTran(sqlList)
    End Function

    ''' <summary>
    ''' 审核结果确认及下发到班长派工yuxj20230327
    ''' 此处派工后需要下发到班长派工界面-班长未派工状态
    ''' </summary>
    ''' <param name="dt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ConfirmApsPlanStatus(ByVal employeeId As String, ByVal dt As DataTable) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim sqlList As New ArrayList
        Dim dtFilter As DataTable = dt.DefaultView.ToTable(True, "containerid") '超过一笔的表示不同策略 不允许一起下发
        For Each drFilter As DataRow In dtFilter.Rows
            'add by longb 2023-11-14 新增一重判断提示，防止另一个策略再次下发
            Dim DwonDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format("select * from  APSPLANINFO where STATUS='1' and containerid='{0}'", drFilter("containerid").ToString))
            If dt.Select(String.Format("containerid='{0}'", drFilter("containerid").ToString)).Length > 1 Or DwonDt.Rows.Count > 0 Then
                Dim containerNo As String = dt.Select(String.Format("containerid='{0}'", drFilter("containerid").ToString))(0)("containername").ToString
                result.Message = "任务单:" + containerNo + "只能下发一个策略的任务"
                Return result
            End If
        Next

        Dim strSqlDetail As String =
            <string>
                select ap.shiftid,ap.containerid,s.workcenterid,ap.specfactoryid factoryid from apsplandetail ap
                left join shift s on s.shiftid=ap.shiftid
                 where ap.apsplaninfoid='{0}' and ap.isuse=0 and ap.status='2' --and rownum=1 modify by longb 2023-12-06
            </string>
        Dim strsqlDisp As String =
            <string>
                select 1 from containerteamdispatch ctd where ctd.containerid='{0}' and isuse=0
            </string>
        Dim strsqlInsert As String =
            <string>
                INSERT INTO CONTAINERTEAMDISPATCH(CONTAINERID,FACTORYID,WORKCENTERID,SHIFTID,CONTAINERTEAMDISPATCHID,EMPLOYEEID,CREATEDATE,ISUSE,ISTRANSFER)
                VALUES('{0}','{1}','{2}','{3}','{4}','{5}',sysdate,0,1)
            </string>
        Dim dtApsDetail As New DataTable
        For Each dr As DataRow In dt.Rows
            dtApsDetail = DatabaseHelper.Query_ReturnDatTable(String.Format(strSqlDetail, dr("apsplaninfoid").ToString))
            If dtApsDetail.Rows.Count = 0 Then
                result.Message = "任务单:" + dr("containername").ToString + "数据异常，没有排产记录"
                Return result
            End If
            sqlList.Add(String.Format("UPDATE APSPLANINFO SET STATUS='1' WHERE apsplaninfoid='{0}' ", dr("apsplaninfoid")))
            sqlList.Add(String.Format("UPDATE apsplandetail ap SET ap.OutEmp = '{0}',ap.OutDate =sysdate,ap.status = '1' where APSPLANINFOID='{1}'", employeeId, dr("apsplaninfoid")))
            '多个工序一起下发到班长派工？但总体班长未派工界面不涉及工序，此处工序上的时间信息在哪儿体现--->直接更新订单分解的数据 or 在生产派工/报工/检验界面上关联查询 或者单独存储
            If DatabaseHelper.Query_ReturnDatTable(String.Format(strsqlDisp, dr("containerid").ToString)).Rows.Count > 0 Then
                '批次已经派工到班，组，不处理
            Else
                sqlList.Add(String.Format("UPDATE ENGINEPARTPLAN EP SET ep.workcenterid='{0}',ep.workcenterdate=sysdate WHERE containerid='{1}' ", dtApsDetail.Rows(0)("WorkCenterID"), dr("containerid")))
                '插入工区派工表--工区排产到班
                If Not dr("workflowtype").Equals("施工工艺") Then 'add by longb 2023-12-06 非施工单
                    sqlList.Add(String.Format(strsqlInsert, dr("ContainerID"), dtApsDetail.Rows(0)("FactoryID"), dtApsDetail.Rows(0)("WorkCenterID"), dtApsDetail.Rows(0)("ShiftID"), System.Guid.NewGuid.ToString, employeeId))
                Else
                    '施工单
                    dtApsDetail = dtApsDetail.DefaultView.ToTable(True, "FactoryID", "ShiftID", "WorkCenterID")
                    For Each ddr As DataRow In dtApsDetail.Rows
                        sqlList.Add(String.Format(strsqlInsert, dr("ContainerID"), ddr("FactoryID"), ddr("WorkCenterID"), ddr("ShiftID"), System.Guid.NewGuid.ToString, employeeId))
                    Next
                End If

                '清理其他类型的排产信息
                sqlList.Add(String.Format("delete APSPLANINFO WHERE apsplaninfoid<>'{0}' and containerid='{1}' ", dr("apsplaninfoid"), dr("ContainerID")))

                '清理其他类型的排产信息
                sqlList.Add(String.Format("delete apsplandetail WHERE apsplaninfoid<>'{0}' and containerid='{1}' ", dr("apsplaninfoid"), dr("ContainerID")))
            End If
        Next
        result.Result = DatabaseHelper.ExecuteSqlListTran(sqlList)
        Return result
    End Function

    ''' <summary>
    ''' 增加颜色判断栏位yuxj20230419
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetEmpApsDataForGrant(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>  select fa.factoryid,c.containername,ad.workflowstepid,ad.workflowstepname,ad.WORKSTATIONID resourceid,
            rd.WORKSTATIONNAME RESNAME,to_char(ad.ResStartTime,'yyyy/mm/dd') stepstart ,to_char(ad.ResEndTime,'yyyy/mm/dd') stepend 
            ,to_char(ad.ResStartTime,'hh24:mi:ss') stepstarthms,to_char(ad.ResEndTime,'hh24:mi:ss') stependhms,ad.ApsPlanDetailid,
            aet.employeeid,e.fullname ,
             case when ad.ResStartTime {0} trunc(ep.plannedstartdate) and ad.resstarttime{1} trunc(sysdate) then '1' --红色
              when ad.ResStartTime {2} trunc(ep.plannedstartdate) and ad.resstarttime {3} sysdate then '2'--黄色
              when ad.resendtime {4} ep.plannedfinishdate then '1' --红色
              else '0' end showcolor--正常    
            from ApsPlanInfo ai
            LEFT JOIN ApsPlanDetail ad ON ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
            left join apsplandetailemp ae on ae.apsplandetailid=ad.apsplandetailid and ae.isuse=0
            LEFT JOIN apsplandetailemptime aet ON aet.apsplandetailempid=ae.apsplandetailempid
            left join containercurrentinfo cci on cci.containerid=ai.containerid
            left join enginepartplan ep on ep.concontainerid=ai.containerid
            left join WORKSTATION rd on rd.WORKSTATIONID =ad.WORKSTATIONID 
            left join factory fa on fa.factoryid=rd.factoryid
            left join employee e on e.employeeid=aet.employeeid
            left join container c on c.containerid=ai.containerid
             where ai.apstype2=2  and rd.WORKSTATIONID is not NULL </string>.Value
        strSql = String.Format(strSql, "<", "<", "<", ">=", ">")
        If para.ContainsKey("ResourceIds") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceIds")) Then
                strSql += String.Format(" and rd.resourceid in ({0}) ", para("ResourceIds"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and rd.factoryid='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("WorkcenterID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkcenterID")) Then
                strSql += String.Format(" and ad.workcenterid='{0}' ", para("WorkcenterID"))
            End If
        End If

        If para.ContainsKey("ShiftID") Then
            If Not String.IsNullOrWhiteSpace(para("ShiftID")) Then
                strSql += String.Format(" and rd.shiftid='{0}' ", para("ShiftID"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and ad.status in ({0}) ", para("Status"))
            End If
        End If

        strSql += " order by ad.workstationid,ad.starttime "
        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '查询涉及分厂所有日历，设备所有日历
        Dim factoryIds = "", resourceIds = ""
        Dim faDt = dt.DefaultView.ToTable(True, "factoryid")
        Dim resDt = dt.DefaultView.ToTable(True, "resourceid")
        For Each row In faDt.Rows
            factoryIds += "'" + row("factoryid").ToString + "',"
        Next
        For Each row In resDt.Rows
            resourceIds += "'" + row("resourceid").ToString + "',"
        Next
        factoryIds = factoryIds.Trim(",")
        resourceIds = resourceIds.Trim(",")

        strSql = <string>select fa.factoryid,md.* from factory fa 
left join makecalendardetail md on md.makecalendarid=fa.makecalendarid
where fa.factoryid in ({0}) and md.makecalendarid is not null order by md.holidaydate asc </string>.Value
        If String.IsNullOrWhiteSpace(factoryIds) Then
            factoryIds = "''"
        End If
        Dim faMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, factoryIds)) '分厂日历

        strSql = <string>select rd.resourceid,md.* from resourcedef rd 
left join makecalendardetail md on md.makecalendarid=rd.makecalendarid
where rd.resourceid in ({0}) and md.makecalendarid is not null order by md.holidaydate asc </string>.Value
        If String.IsNullOrWhiteSpace(resourceIds) Then
            resourceIds = "''"
        End If
        Dim resMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, resourceIds)) '设备日历

        '二次处理，将日期分割为一天一天
        Dim result = dt.Clone
        For Each row As DataRow In dt.Rows
            Dim orgStepStart = Date.Parse(row("stepstart").ToString)
            row("stepstart") = Date.Parse(row("stepstart"))
            Dim diffDays = (Date.Parse(row("stepend").ToString).Date - Date.Parse(row("stepstart").ToString).Date).Days
            If diffDays > 0 Then

                Dim orgStepEndHms = row("stependhms").ToString

                For i As Integer = 0 To diffDays
                    Dim reRow As DataRow = result.NewRow
                    reRow("resourceid") = row("resourceid")
                    reRow("RESNAME") = row("RESNAME")
                    reRow("ApsPlanDetailid") = row("ApsPlanDetailid")
                    reRow("containername") = row("containername")
                    reRow("workflowstepid") = row("workflowstepid")
                    reRow("workflowstepname") = row("workflowstepname")
                    reRow("factoryid") = row("factoryid")
                    reRow("employeeid") = row("employeeid")
                    reRow("fullname") = row("fullname")
                    reRow("showcolor") = row("showcolor") '增加颜色标识yuxj
                    If i = 0 Then
                        reRow("stepstarthms") = row("stepstarthms")
                    Else
                        reRow("stepstarthms") = "00:00:00"
                    End If
                    reRow("stepstart") = orgStepStart.AddDays(i)

                    reRow("stepend") = orgStepStart.AddDays(1 + i).AddMinutes(-1)


                    If i < diffDays Then
                        reRow("stependhms") = "23:59:00"
                    Else
                        reRow("stependhms") = orgStepEndHms
                    End If

                    reRow("stepstarthms") = Date.Parse(reRow("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stepstarthms").ToString
                    reRow("stependhms") = Date.Parse(reRow("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stependhms").ToString
                    '判断是否是假期 
                    If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    result.Rows.Add(reRow)
                Next

            Else
                row("stepend") = Date.Parse(row("stepstart").ToString).AddDays(1).AddMinutes(-1)
                row("stepstarthms") = Date.Parse(row("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stepstarthms").ToString
                row("stependhms") = Date.Parse(row("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stependhms").ToString
                '判断是否是假期
                If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                result.ImportRow(row)
            End If


        Next
        result.AcceptChanges()

        Dim temp = result.AsEnumerable()
        Dim temp2 = temp.OrderBy(Function(r) Date.Parse(r("stepstarthms"))) '转化日期后排序

        'yuxj20220826本方法有下发排产计划时才计算，处理查询结果为空时报错bug
        If result.Rows.Count > 0 Then
            result = temp2.CopyToDataTable
        End If


        Return result

    End Function
#End Region
    ''' <summary>
    ''' 总体排产计算
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="dtPolicy"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ZSAPSCalculate(ByVal conDt As DataTable, ByVal dtPolicy As DataTable, ByVal paraEmp As Dictionary(Of String, String)) As uMESProcessResult
        '增加逻辑换型时间changeOverCycle 以录入为准，若不录，换型时间=准备时间+转运时间。装试用
        '批次工序下发，必须按照由先到后的顺序进行 
        '本次选择的批次排产时, 处理掉 本次排产策略下&本次所选批次之前在该排产策略下所有没有下发的数据
        '本次选择的批次排产时，各批次在工位占用时间上采用的是迭代占用的形式
        '重要：相同对象的批次组 可以并存，不同对象的批次组 后者可将前者的排产结果冲销掉
        '例如:批次1,2,3 正排 产生结果，批次1,2,3 倒排 产生结果  此时两结果并存
        '批次1,2,3 正排 产生结果，批次1,2,3 倒排 产生结果，此时批次4,5,6或者批次1,5,6正排时，将1,2,3的正排和倒排结果统统冲销

        '1级策略 订单优先级 及齐套已经预处理
        '2级策略根据一级策略自动绑定
        '3级策略自由选择
        '4级策略在1级策略为齐套时只用空间时间优先

        '业务逻辑：
        '工序绑定工位，一个工序可绑定多个工位（通常是一个），多个工序可以绑定一个工位
        '相同平行工序号的工序可以一起排配（时间可以交叉）
        '工位绑定到组，组绑定到班次，人员绑定到组
        '一个组中所有人员默认到同一班次(组的班次)
        '每道工序需要绑定工位，每道工序需要设置 准备时间+加工时间+转运时间
        '负载均衡与空闲时间优先：一个工位被占用后查看是否有其他空闲工位可用，都被占用后取最近能用的工位
        '占用有被当前排产数据占用的情况
        '平行工序不能设置为同一个工位
        '工序齐套中，只排前面连续齐套的工序
        '一道工艺只能有一个瓶颈工位
        '==================================》调整20221019：
        '1，瓶颈工位调整为瓶颈工序
        '2，工位和班进行关联
        '3，班和班次关联
        '4，日历和分厂关联 通过workflow factoryid获取分厂
        '5，组的日历继承自班，组可以有自身的日历----》班的班次应该包含组的班次总和
        '6，在排产界面增加预齐套时间，对没有齐套的任务单，设置预齐套时间后可以理解为已经齐套，可按照齐套的逻辑排产

        '获取分厂日历 -日历查询语句
        Dim makeSql As String =
            <string>
                select null resourceid, ml.* from MakeCalendarDetail ml
                left join factory f on f.makecalendarid=ml.makecalendarid
                 where f.factoryid='{0}' and ml.isuse=0 
            </string>
        faMakeCalendarDt = DatabaseHelper.Query_ReturnDatTable(String.Format(makeSql, paraEmp("FactoryID")))

        Dim result = New uMESProcessResult
        result.Result = False
        '记录策略
        Dim drPolicy As DataRow = dtPolicy.Rows(0)
        Dim policy1 As String = drPolicy("policy1").ToString() 'Ⅰ级排产策略（0：订单优先级，1：齐套)
        Dim policy2 As String = drPolicy("policy2").ToString() 'Ⅱ级排产策略（2：工序齐套，3：瓶颈工序）
        Dim policy3 As String = drPolicy("policy3").ToString() 'Ⅲ级排产策略（4：倒序，5：正序）
        Dim policy4 As String = drPolicy("policy4").ToString() 'Ⅳ级排产策略（6：负载均衡，7：空闲时间优先）
        Dim strPolicy As String = policy1 + policy2 + policy3 + policy4
        '查询工序，工位，瓶颈工位，平行工序号
        '------->瓶颈工位改为瓶颈工序，日历信息从工厂factory取,组的班次默认从班上继承
        '转运工时不参与设备运行时间的计算，但参与工序总体时间的计算
        Dim strsql As String =
            <string>
                 select  wfb.workflowname,wfs.workflowstepname,wfs.workflowstepid,f.makecalendarid,
                        wfs.parallelgroupname,wfs.isbottleneck,wfs.sequence,
                        wfs.PrepareCycle,wfs.MakeCycle,wfs.TransferCycle,wfs.workstationids,wfs.teamid,wfs.workcenterid  
                from workflowstep wfs                
                left join workflow wf on wfs.workflowid=wf.workflowid
                left join workflowbase wfb on wfb.workflowbaseid=wf.workflowbaseid   
                left join factory f on wf.factoryid=f.factoryid             
                where wfs.workflowid='{0}' order by wfs.sequence asc
            </string>

        '总体工位和班
        Dim resShift = <string> select distinct t.shiftid
                                    from workstation ws
                                    left join shift t on t.shiftid=ws.shiftid                                       
                                    where ws.workstationid in ('{0}') </string>.Value

        Dim dtWorkFlow As New DataTable
        For Each dr As DataRow In conDt.Rows
            Dim containerno As String = dr("containerno")
            Dim workflowid As String = dr("workflowid")
            dtWorkFlow = DatabaseHelper.Query_ReturnDatTable(String.Format(strsql, workflowid))
            If dtWorkFlow.Rows.Count > 0 Then
                '需要工序绑定工位
                If dtWorkFlow.Select("workstationids is null").Length > 0 Then
                    result.Message = "任务单:" + containerno + "有工序没有没有绑定工位"
                    Return result
                Else
                    '瓶颈工序策略
                    If policy2 = "3" Then
                        If dtWorkFlow.Select("isbottleneck='1'").Length = 0 Then
                            '修改为没有瓶颈工序时不退出
                            result.Message = "任务单:" + containerno + "没有设置瓶颈工序"
                            'Return result
                        End If

                        '选瓶颈策略时只能有一个瓶颈 
                        If dtWorkFlow.Select("isbottleneck='1'").Length > 1 Then
                            result.Message = "任务单:" + containerno + "只能指定一个瓶颈工序"
                            Return result
                        End If
                    End If
                End If

                '检查时间是否设置正确
                For Each drStep As DataRow In dtWorkFlow.Rows
                    Try
                        Dim stepCycle As Single = Single.Parse(drStep("transfercycle").ToString) + Single.Parse(drStep("makecycle").ToString) +
                                                Single.Parse(drStep("preparecycle").ToString)
                    Catch
                        result.Message = "任务单:" + containerno + "工序" + drStep("workflowstepname") + "准备工时|加工工时|运转工时格式错误或没有设置"
                        Return result
                    End Try

                    Dim stationlist As String = drStep("workstationids")
                    '此工序工位所有的班信息
                    Dim stepShiftDt = DatabaseHelper.Query_ReturnDatTable(String.Format(resShift, stationlist.Replace(",", "','")))
                    If stepShiftDt.Rows.Count > 1 Then
                        result.Message = "一道工序:" + drStep("workflowstepname") + " 只能在一个班进行加工"
                        Return result
                    End If
                Next
            Else
                result.Message = "任务单:" + containerno + "没有工序信息"
                Return result
            End If
        Next
        '获取排产信息
        Dim strApsIdList As String = GetNeedKeepAps(conDt, strPolicy)
        '清理无效的排产信息
        ClearUselessAps(strApsIdList, paraEmp("FactoryID"))

        containersApsId = System.Guid.NewGuid().ToString '本次排产的批次共用
        Dim dtStep As New DataTable '批次工序信息
        Dim dtStation As New DataTable '工位信息
        Dim dtApsTemp As New DataTable '本次排产进入数据库前临时信息
        dtApsTemp.Columns.Add("starttime")
        dtApsTemp.Columns.Add("endtime")
        dtApsTemp.Columns.Add("workstationid")
        dtApsTemp.Columns.Add("containerid")
        dtApsTemp.Columns.Add("shiftid")
        For Each dr As DataRow In conDt.Rows
            Dim containerno As String = dr("containerno")
            Dim workflowid As String = dr("workflowid")
            Dim containerid As String = dr("containerid")
            Dim ExpectMaterialDate As String = "" '预计齐套时间
            If Not IsDBNull(dr("ExpectMaterialDate")) Then
                ExpectMaterialDate = dr("ExpectMaterialDate").ToString
            End If
            dtStep = DatabaseHelper.Query_ReturnDatTable(String.Format(strsql, workflowid)) '工序列表   

            '增加过滤 已经派工的工序不参与排产,最大派工序之前的工序不参与排产yuxj
            Dim dtStepClone As DataTable = dtStep.Clone()
            Dim dispatchSql = "select containerid from containerstepdispatch csd where csd.isuse=0 and csd.containerID='{0}' and csd.workflowstepid='{1}'"
            For i As Integer = dtStep.Rows.Count - 1 To 0 Step -1
                If (DatabaseHelper.Query_ReturnDatTable(String.Format(dispatchSql, dr("containerid"), dtStep.Rows(i)("workflowstepid"))).Rows.Count = 0) Then
                    dtStepClone.Rows.Add(dtStep(i).ItemArray)
                Else
                    Exit For '
                End If
            Next

            If dtStepClone.Rows.Count > 0 Then
                Dim dv As DataView = dtStepClone.DefaultView
                dv.Sort = " sequence asc "
                dtStep = dv.ToTable()
            Else
                '没有需要排产的工序,排其他批次
                Exit For
            End If

            If policy2 = "2" Then '工序齐套
                Dim para As New Dictionary(Of String, String)()
                para.Add("ContainerNo", dr("containerNo").ToString())
                para.Add("ContainerID", dr("containerid").ToString())
                para.Add("workflowid", dr("workflowid").ToString())
                para.Add("mfgorderid", dr("mfgorderid").ToString()) 'add by longb 2023-09-01
                Dim dtt As DataTable = m_fullab.GetSpec(para)
                Dim spec As DataView = dtt.DefaultView
                spec.RowFilter = "PCOUNT<>CCOUNT" '没有齐套的工序
                dtt = spec.ToTable()
                If dtt.Rows.Count > 0 Then
                    Dim i_seq As Integer = Convert.ToInt32(dtt.Rows(0)("SEQUENCE").ToString)
                    spec = dtStep.DefaultView
                    'spec.RowFilter = String.Format("SEQUENCE<{0}", i_seq) '没有齐套工序中最小的工序 之前的工序参与排产
                    If spec.ToTable.Rows.Count = 0 And ExpectMaterialDate = "" Then
                        result.Message = "任务单:" + containerno + "没有工序有配套信息"
                        Return result
                        'Continue For
                    Else
                        dtStep = spec.ToTable() '没有齐套工序中最小的工序 之前的工序参与排产yuxj20230411
                    End If
                End If
            End If

            '获取瓶颈工序(正常应该只有一个工序是瓶颈)
            Dim BottleneckSeq As Integer = 0
            '会有一个瓶颈工序挂多个工位，工位上标识瓶颈
            If dtStep.Select("isbottleneck='1'").Length = 1 Then
                BottleneckSeq = dtStep.Select("isbottleneck='1'")(0)("sequence")
            Else
                'BottleneckSeq = 0 '修改为没有瓶颈工序时不退出,暂指定默认值第一序
            End If

            If policy2 = "2" Then
                '不采用齐套策略时，不考虑瓶颈工序
                BottleneckSeq = 0
            End If

            '修改为没有瓶颈工序时不退出，如下不会执行
            If policy2 = "3" And BottleneckSeq = 0 Then
                'result.Message = "任务单:" + containerno + "没有瓶颈工序"
                'Return result
            End If

            Dim plannedfinishdate As String = dr("plannedfinishdate") '结束时间
            Dim plannedstartdate As String = dr("plannedstartdate") '开始时间
            '如果有齐套时间，将到料时间设置为开始时间
            '没有任务单的不设置齐套时间，没有齐套的此栏存预计齐套时间，所有齐套完成的存实际齐套时间
            If policy2 = "3" Then
                If ExpectMaterialDate > plannedstartdate Then
                    plannedstartdate = ExpectMaterialDate
                End If
            End If

            Dim _startDate = Date.Parse(plannedstartdate) '初始开始时间
            Dim _endDate = Date.Parse(plannedfinishdate) '初始结束时间

            If policy3 = 4 Then '倒序
                _endDate = _endDate.AddDays(GetDateOffset(2, _endDate, faMakeCalendarDt, 0)) '检查工作日历
            Else '正序
                _startDate = _startDate.AddDays(GetDateOffset(1, _startDate, faMakeCalendarDt, 0)) '检查工作日历
            End If

            Dim stepPlanSqls As New ArrayList
            CalcApsStartEndTime(stepPlanSqls, containerid, workflowid, _startDate, _endDate, dtApsTemp, dtStep, drPolicy, BottleneckSeq, paraEmp)
        Next
        result.Result = True
        Return result
    End Function

    ''' <summary>  
    ''' 相同对象=所选的批次相同，所选的策略策略不同的需要保留
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="strPolicy"></param>
    ''' <remarks></remarks>
    Private Function GetNeedKeepAps(ByVal conDt As DataTable, ByVal strPolicy As String) As String
        '获取批次列表
        Dim dv As DataView = conDt.Copy.DefaultView
        dv.Sort = "containerid asc"
        Dim idList As String = "" '源批次id列表
        For Each dr As DataRow In dv.ToTable().Rows
            idList = idList + dr("containerid").ToString + ","
        Next
        idList = idList.Substring(0, idList.Length - 1)

        '先取第一个批次id根据策略获取包含本批次的apsid列表，
        '再判断apsid列表中批次和本次勾选批次是否为同一对象(先增加一次过滤，目的为减少计算量)
        '寻找策略不同，批次数相同的对象，此部分排产记录需要保留
        '总体分厂使用
        Dim strApsIdsql As String =
            <string>
                select listagg(apz.apsid,',') within group(order by apz.apsid asc) apsIdList 
                from apsplaninfo apz where apz.apstype!='{0}' and apz.isuse=0 and apz.containerid='{1}'                
            </string>
        strApsIdsql = String.Format(strApsIdsql, strPolicy, conDt.Rows(0)("containerid").ToString)
        Dim dtIdlist As DataTable = DatabaseHelper.Query_ReturnDatTable(strApsIdsql)
        Dim apsIdList As String = dtIdlist.Rows(0)("apsIdList").ToString
        If apsIdList = String.Empty Then
            Return ""
        End If

        Dim strConIdsql As String =
            <string>
                select apz.apsid,apz.apstype,listagg(apz.containerid,',') within group(order by apz.containerid asc) conIdList 
                from apsplaninfo apz where apz.apsid in ('{0}') and apz.isuse=0
                group by apz.apsid,apz.apstype
            </string>
        strConIdsql = String.Format(strConIdsql, apsIdList.Replace(",", "','"))
        Dim dtConIdlist As DataTable = DatabaseHelper.Query_ReturnDatTable(strConIdsql)

        '不需要处理的排产记录
        For i As Integer = dtConIdlist.Rows.Count - 1 To 0 Step -1
            If dtConIdlist.Rows(i)("conIdList").ToString = String.Empty Then
                Return ""
            End If
            '批次不相等
            If idList <> dtConIdlist.Rows(i)("conIdList").ToString Then
                dtConIdlist.Rows.RemoveAt(i)
            End If
        Next

        '获取需要保留的排产记录，同对象不同策略
        Dim strApsPlansql As String =
            <string>
                select ap.apsplaninfoid from apsplaninfo ap 
                where ap.apstype='{0}' and ap.apstype2='2' and ap.isuse=0 and ap.containerid in ('{1}') 
            </string>
        Dim dtApsPlanInfo As New DataTable
        For Each dr As DataRow In dtConIdlist.Rows
            If dtApsPlanInfo.Rows.Count = 0 Then
                dtApsPlanInfo = DatabaseHelper.Query_ReturnDatTable(String.Format(strApsPlansql, dr("apstype").ToString, idList.Replace(",", "','")))
            Else
                dtApsPlanInfo.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(strApsPlansql, dr("apstype").ToString, idList.Replace(",", "','"))))
            End If
        Next

        '获取需要保留的排产id 即apsplaninfoid
        Dim apsplaninfoidList As String = ""
        For Each dr As DataRow In dtApsPlanInfo.Rows
            apsplaninfoidList = apsplaninfoidList + dr("apsplaninfoid").ToString + ","
        Next

        If apsplaninfoidList.Length > 0 Then
            apsplaninfoidList = apsplaninfoidList.Substring(0, apsplaninfoidList.Length - 1)
        End If

        Return apsplaninfoidList
    End Function

    ''' <summary>
    ''' 清理无效的排产记录
    ''' 在本次排产时，相同对象不同策略的可并行存在，不同对象不管策略是否相同(只要未下发)均清除
    ''' 相同对象=所选的批次相同，所选的策略相同
    ''' </summary>
    ''' <param name="apsIdList"></param>
    ''' <remarks></remarks>
    Private Sub ClearUselessAps(ByVal apsIdList As String, ByVal FactoryID As String)
        '清除操作 本分厂只要没下发的 非本对象的一概清除
        Dim sqlList As New ArrayList
        Dim deleteDetailSql As String =
            <string>
                delete from apsplandetail apt
                where apt.specfactoryid='{0}' 
                and apt.isuse=0 and nvl(apt.status,'0')='0'                
            </string>

        If apsIdList <> String.Empty Then
            deleteDetailSql = deleteDetailSql + String.Format(" and apt.apsplaninfoid not in ('{0}')", apsIdList.Replace(",", "','"))
        End If
        sqlList.Add(String.Format(deleteDetailSql, FactoryID))
        '清除基本表 '部分下发时基本表不清除
        Dim deletePlaninfoSql As String =
             <string>
                delete from apsplaninfo ap 
                where not exists
                (select 1 from apsplandetail apt where apt.isuse=0 
                 and apt.specfactoryid='{0}' and apt.apsplaninfoid=ap.apsplaninfoid)
                 and length(ap.apstype)=4 and nvl(ap.status,'0')='0'
            </string>

        '清除 总体排产到班，只用处理apsplandetail，apsplaninfo，已经下发的数据不能处理
        '清除不是本对象，且没有下发的，本厂的数据
        sqlList.Add(String.Format(deletePlaninfoSql, FactoryID))
        DatabaseHelper.ExecuteSqlTran(sqlList)
      
    End Sub
    ''' <summary>
    ''' 计算每道工序用什么工位 及开始结束时间
    ''' </summary>
    ''' <param name="starttime"></param>
    ''' <param name="endtime"></param>
    ''' <param name="dtApsTemp">存本次排产临时信息，处理策略4</param>
    ''' <param name="dtStep">工序信息</param>
    ''' <param name="drPolicy">策略</param>
    ''' <param name="BottleneckSeq">瓶颈工序顺序</param>
    ''' <remarks></remarks>
    Private Sub CalcApsStartEndTime(ByRef stepPlanSqls As ArrayList, ByVal containerid As String, ByVal workflowid As String,
                                    ByVal starttime As Date, ByVal endtime As Date, ByRef dtApsTemp As DataTable, ByVal dtStep As DataTable,
                                    ByVal drPolicy As DataRow, ByVal BottleneckSeq As String, ByVal para As Dictionary(Of String, String))
        Dim _beginDate As Date
        Dim policylist As String = drPolicy("Policy1").ToString() + drPolicy("Policy2").ToString() + drPolicy("Policy3").ToString() + drPolicy("Policy4").ToString()
        If BottleneckSeq > 0 Then '瓶颈排
            '瓶颈正排 将开始时间赋予瓶颈工序
            '瓶颈倒排 将结束时间赋予瓶颈工序

            '瓶颈工序            
            Dim dtStepFront As New DataTable '瓶颈前，倒序
            Dim dtStepBack As New DataTable '瓶颈后，顺序
            If drPolicy("policy3").ToString() = "5" Then '正排
                _beginDate = starttime
                Dim apsplaninfoid As String = "0"
                If BottleneckSeq = 1 Then
                    apsplaninfoid = ""
                End If

                dtStepBack = dtStep.Select(String.Format("sequence>={0}", BottleneckSeq)).CopyToDataTable
                PositiveCalculate(dtStepBack, _beginDate, policylist, containerid, workflowid, para, apsplaninfoid)

                If BottleneckSeq > 1 Then
                    '第一序不为瓶颈
                    dtStepFront = dtStep.Select(String.Format("sequence<{0}", BottleneckSeq)).CopyToDataTable
                    ReverseCalculate(dtStepFront, _beginDate, policylist, containerid, workflowid, para, apsplaninfoid)
                Else
                    '第一序为瓶颈
                End If

            Else '倒排
                Dim apsplaninfoid As String = "0"

                '最后一序为瓶颈
                If BottleneckSeq = dtStep.Rows.Count Then
                    apsplaninfoid = ""
                End If

                _beginDate = endtime

                dtStepFront = dtStep.Select(String.Format("sequence<={0}", BottleneckSeq)).CopyToDataTable
                ReverseCalculate(dtStepFront, _beginDate, policylist, containerid, workflowid, para, apsplaninfoid)

                If BottleneckSeq < dtStep.Rows.Count Then
                    dtStepBack = dtStep.Select(String.Format("sequence>{0}", BottleneckSeq)).CopyToDataTable
                    PositiveCalculate(dtStepBack, _beginDate, policylist, containerid, workflowid, para, apsplaninfoid)
                Else
                    '最后一序为瓶颈
                End If

            End If

            '_beginDate = starttime
            'PositiveCalculate(dtStepBack, _beginDate, policylist, containerid, workflowid, para)

            '_beginDate = endtime
            'ReverseCalculate(dtStepFront, _beginDate, policylist, containerid, workflowid, para)
        Else
            If drPolicy("policy3").ToString() = "5" Then '正排
                _beginDate = starttime
                PositiveCalculate(dtStep, _beginDate, policylist, containerid, workflowid, para)
            Else '倒排
                _beginDate = endtime
                ReverseCalculate(dtStep, _beginDate, policylist, containerid, workflowid, para)
            End If
        End If
    End Sub

    ''' <summary>
    ''' 从工位中找相对空闲工位，历史数据+本次排产?
    ''' </summary>
    ''' <param name="dtApsTemp"></param>
    ''' <param name="drsStation"></param>
    ''' <param name="_beginDate"></param>
    ''' <param name="_endDate"></param>
    ''' <param name="ReverseFlag"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetUsefulStation(ByRef dtApsTemp As DataTable, ByVal drsStation As DataRow(), ByRef _beginDate As Date, ByRef _endDate As Date, ByVal ReverseFlag As Boolean) As DataRow
        '从数据库和临时表中取最大结束时间作开始时间（采用事务，多台电脑同时排产，排到了同一工位的情况不考虑--从业务看，理论上也不应该发生）
        Dim stationlist As String = ""
        For Each drStation As DataRow In drsStation
            stationlist += drsStation("workstationid").ToString + ","
        Next

        If stationlist.Length > 0 Then
            stationlist = stationlist.Substring(0, stationlist.Length - 1)
        End If

        Dim sqljoin As String = ""
        '不包含自身批次(Status=1过滤)
        Dim strsql As String =
            <string>
                select apt.starttime,apt.endtime,apt.resourceid workstationid,apt.teamid from apsplandetail apt 
                where apt.ResourceID in ('{0}') and apt.isuse=0 and apt.Status=1                 
            </string>
        strsql = String.Format(strsql, stationlist.Replace(",", "','"))
        If ReverseFlag Then
            sqljoin = " AND ( apt.endtime<= to_date('{0}','yyyy-mm-dd hh24:mi:ss') or (apt.starttime<to_date('{0}','yyyy-mm-dd hh24:mi:ss') and apt.endtime>to_date('{0}','yyyy-mm-dd hh24:mi:ss') ) "
            sqljoin += String.Format(sqljoin, _endDate)
            strsql += sqljoin
            strsql += " order by apt.endtime desc"
        Else
            sqljoin = " AND ( apt.starttime> to_date('{0}','yyyy-mm-dd hh24:mi:ss') or (apt.starttime<to_date('{0}','yyyy-mm-dd hh24:mi:ss') and apt.endtime>to_date('{0}','yyyy-mm-dd hh24:mi:ss') ) "
            sqljoin += String.Format(sqljoin, _beginDate)
            strsql += sqljoin
            strsql += " order by apt.starttime asc"
        End If

        Dim dtUse As DataTable = DatabaseHelper.Query_ReturnDatTable(strsql) '被占用的时段
        For Each drStation As DataRow In drsStation
            Dim drsUse As DataRow() = dtUse.Select(String.Format("workstationid='{0}'", drsStation("workstationid").ToString))
            Dim drsApsTemp As DataRow() = dtApsTemp.Select(String.Format("workstationid='{0}'", drsStation("workstationid").ToString))
            If drsUse.Length = 0 And drsApsTemp.Length = 0 Then
                '空闲时间优先，该工位没有被占用，先使用此工位
                dtApsTemp.Rows.Add(_beginDate, _endDate, drStation("workstationid"), drStation("teamid"))
                Return dtApsTemp.Rows(dtApsTemp.Rows.Count - 1)
            ElseIf drsUse.Length = 0 Then
                Return drsApsTemp(0)
            Else
                Return drsUse(0)
            End If
        Next
        '下面分支有工位即不会发生
        Return Nothing
    End Function

    ''' <summary>
    ''' 日期偏移量，用于假期时自动偏移
    ''' </summary>
    ''' <param name="type">正序:1或倒序:2</param>
    ''' <param name="d">日期</param>
    ''' <param name="makeCalendarDt">日历</param>
    ''' <param name="add">日期再次叠加量</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetDateOffset(ByVal type As Integer, ByVal d As Date, ByVal makeCalendarDt As DataTable, ByVal add As Integer) As Integer
        Dim offset = 0 '日期偏移量
        If type = 1 Then '正序

            While makeCalendarDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = d.AddDays(offset + add).Date).AsDataView.Count > 0
                offset += 1
            End While

            'While makeCalendarDt.Select(String.Format("Holidaydate='{0}'", d.AddDays(offset + add).Date)).Length > 0
            '    offset += 1
            'End While
            Return offset

        ElseIf type = 2 Then '倒序
            While makeCalendarDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = d.AddDays(-(offset + add)).Date).AsDataView.Count > 0
                offset += 1
            End While

            'While makeCalendarDt.Select(String.Format("Holidaydate='{0}'", d.AddDays(-(offset + add)).Date)).Length > 0
            '    offset += 1
            'End While
            Return -offset
        Else
            Return 0
        End If

    End Function

    ''' <summary>
    ''' 负载均衡与空闲时间优先 负载均衡策略只在一个工序对应多个工位时有效
    ''' </summary>
    ''' <param name="policylist">策略</param>
    ''' <param name="stationlist">工位列表子串</param>
    ''' <param name="stepResourceIdList">工位列表</param>
    ''' <remarks></remarks>
    Private Sub CalcLoadAVG(ByVal policylist As String, ByRef stationlist As String, ByRef stepResourceIdList As List(Of String))
        '负载均衡与空闲时间优先 负载均衡策略只在一个工序对应多个工位时有效
        '同批次的不同工序如果适用负载均衡策略，所有工序可对应一个工位，否则可能出现后面的工序排产在前的情况（或者可以按时间后移）
        '不同批次适用负载均衡时，根据任务量安排工位
        If (policylist.Length = 4) And (stepResourceIdList.Count >= 2) Then
            Dim policy4 As String = policylist.Substring(3, 1) 'Ⅳ级排产策略（6：负载均衡，7：空闲时间优先）
            If policy4 = 6 Then
                '取任务个数最小的一条 可能为0
                Dim strTempSql As String =
                    <string>
                            select apt.workstationid,apt.apsplaninfoid from apsplandetail apt left join apsplaninfo ap on apt.apsplaninfoid=ap.apsplaninfoid
                            where ap.apstype='{0}' and nvl(apt.status,'0')='0' and apt.isuse=0 and apt.workstationid in ('{1}') 
                        </string>
                Dim dtTemp As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(strTempSql, policylist, stationlist.Replace(",", "','")))

                If dtTemp.Rows.Count > 0 Then
                    Dim i_length As Integer = 0
                    Dim strStationTemp As String = ""
                    For Each strStation As String In stationlist.Split(",")
                        If dtTemp.Select(String.Format("workstationid='{0}'", strStation)).Length = 0 Then
                            strStationTemp = strStation
                            Exit For
                        End If

                        If i_length = 0 Then '赋初值
                            i_length = dtTemp.Select(String.Format("workstationid='{0}'", strStation)).Length
                            strStationTemp = strStation
                        End If

                        If dtTemp.Select(String.Format("workstationid='{0}'", strStation)).Length < i_length Then '赋新值
                            i_length = dtTemp.Select(String.Format("workstationid='{0}'", strStation)).Length
                            strStationTemp = strStation
                        End If
                    Next
                    stationlist = strStationTemp
                Else
                    '取其一
                    stationlist = stepResourceIdList(0)
                End If
                stepResourceIdList.Clear()
                stepResourceIdList.Add(stationlist)
            End If
        End If
    End Sub

    ''' <summary>
    ''' 正排
    ''' </summary>
    ''' <param name="dtStep"></param>
    ''' <param name="_beginDate"></param>
    ''' <param name="policylist"></param>
    ''' <param name="containerid"></param>
    ''' <param name="workflowid"></param>
    ''' <param name="para"></param>
    ''' <remarks></remarks>
    Sub PositiveCalculate(ByVal dtStep As DataTable, ByVal _beginDate As Date, ByVal policylist As String,
                          ByVal containerid As String, ByVal workflowid As String, ByVal para As Dictionary(Of String, String), Optional ByRef apsplaninfoid As String = "")

        '批次级排产信息sql
        Dim conSelectSql = <string> select * from ApsPlanInfo ai where ai.containerid='{0}' and ai.workflowid='{1}' and ai.ApsType2=2 and ai.ApsType='{2}' and ai.Isuse=0 </string>.Value
        Dim conInsertSql = <STIRNG> insert into ApsPlanInfo ai (ai.ApsType2,ai.apsplaninfoid,ai.apstype,ai.containerid,ai.createdate,ai.createemployeeid,ai.endtime,ai.isuse,
 ai.lastchangedate,ai.notes,ai.starttime,ai.workflowid,apsid) values (2,'{0}','{1}','{2}',sysdate,'{3}',to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
 SYSDATE,'{5}',to_date('{6}','YYYY-MM-DD HH24:MI:SS'),'{7}','{8}') </STIRNG>.Value
        Dim conUpdateSql = <string>update ApsPlanInfo ai set ai.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS') , ai.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'), 
                                    ai.lastchangedate=sysdate,apsid='{2}' where ai.apsplaninfoid='{3}'</string>.Value

        '工序级排产信息sql
        Dim stepSelectSql = <string>select * from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.ContainerID='{1}' and ad.isuse=0 </string>.Value
        Dim stepInsertSql = <string> insert into ApsPlanDetail ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
                                ad.workflowstepid,ad.workflowstepname,ad.notes,ad.workcenterid,ad.teamid,ad.workstationid,CreateEmployeeID,ad.SpecFactoryID,shiftid,ad.Resstarttime,ad.Resendtime,resourceid) 
                                values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
                                '{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}',to_date('{14}','YYYY-MM-DD HH24:MI:SS'),to_date('{15}','YYYY-MM-DD HH24:MI:SS'),'{16}') </string>.Value
        Dim stepUpdateSql = <string>    update ApsPlanDetail ad set  AD.Status=0,ad.FeedbackDate=null,ad.ConfirmDate=null,
                                        ad.ConfirmEmpID=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.workcenterid='{2}',ad.teamid='{3}',ad.workstationid='{4}',ad.SpecFactoryID='{5}',
                                        ad.shiftid='{6}',ad.Resstarttime=to_date('{7}','YYYY-MM-DD HH24:MI:SS'),ad.Resendtime=to_date('{8}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.resourceid='{9}' 
                                        where ad.apsplaninfoid='{10}' and ad.workflowstepid='{11}' </string>.Value

        '通过工序级排产信息，计算批次的开始，结束时间
        Dim stepSelectSql2 = <string>select ad.containerid,min(ad.starttime) minstarttime,max(ad.endtime) maxendtime from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.isuse=0 group by ad.containerid </string>.Value

        '设备占用信息,当前结束时间之前,查询这些设备被排产后占用的情况，结果集需要排除已完工的
        Dim resOccupyAps =
            "select ad.* from ApsPlanDetail ad left join enginepartplan ep on nvl(ep.containerid,ep.enginecontainerid)=ad.containerid " +
            "where ad.workstationid in ('{0}') and ad.isuse=0 and ad.Status in (1,2) " +
            "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
            "or ( " +
            "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
            ") ) and ep.usecompleted='0' "
        '因本次排产未下发的批次间工位占用迭代执行，故此处并上本次排产的未下发部分yuxj20230301
        resOccupyAps = resOccupyAps + " union all " +
            " select apt.* from apsplandetail apt left join apsplaninfo ap on apt.apsplaninfoid=ap.apsplaninfoid where ap.apstype='{2}' and nvl(apt.status,'0')='0' and apt.isuse=0 " +
            "and apt.workstationid in ('{0}') " '+
        '"and ( apt.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '"or ( " +
        '"apt.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and apt.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '") )"

        '设备班次信息 总体工位和班关联
        Dim resWorkShift = <string> select t.workcenterid, ws.workstationid resourceid,t.shiftid, wf.workshiftid,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence 
                                    from workstation ws
                                    left join shift t on t.shiftid=ws.shiftid   
                                    left join workshift wf on wf.workshiftid=t.workshiftid and wf.isuse=0
                                    left join workshiftdetail wd on wd.workshiftid=wf.workshiftid
                                    where ws.workstationid in ('{0}') 
                                    order by wf.workshiftid, wd.sequence asc </string>.Value

        Dim startTime = _beginDate
        Dim curTime = startTime

        Dim stepCycle As Single = 0 '工序加工周期，单位分钟
        Dim stepCyCle2 As Single = 0 '设备加工周期，单位分钟
        Dim stepPlanSqls As New ArrayList
        Dim conApsID = Guid.NewGuid.ToString

        '外面传入的ApsPlanInfoID
        If apsplaninfoid <> "" And apsplaninfoid <> "0" Then
            conApsID = apsplaninfoid
        End If

        Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(conSelectSql, containerid, workflowid, policylist)) '批次的计划信息
        If conApsDt.Rows.Count > 0 Then
            conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
        End If

        '批次工序的计划信息
        Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(stepSelectSql, conApsID, containerid))

        Dim stepResUpkeepDt As New DataTable
        stepResUpkeepDt.Columns.Add("resourceid")
        stepResUpkeepDt.Columns.Add("upkeepstartdate")
        stepResUpkeepDt.Columns.Add("upkeependdate")
        For Each drStep As DataRow In dtStep.Rows
            Dim _startTime = curTime '此工序的初期开始时间
            Dim stepApsRow = stepApsDt.Select(String.Format(" workflowstepid='{0}' ", drStep("workflowstepid").ToString))
            If stepApsRow.Length > 0 Then
                If stepApsRow(0)("Status").ToString = "1" Then
                    curTime = Date.Parse(stepApsRow(0)("EndTime").ToString) '已下发工序的结束时间
                    '检查工作日历
                    curTime = curTime.AddDays(GetDateOffset(1, curTime, faMakeCalendarDt, 0))
                    Continue For
                End If
            End If
            stepCycle = Single.Parse(drStep("transfercycle").ToString) + Single.Parse(drStep("makecycle").ToString) +
                 Single.Parse(drStep("preparecycle").ToString)
            stepCyCle2 = Single.Parse(drStep("makecycle").ToString) + Single.Parse(drStep("preparecycle").ToString)

            Dim stepid As String = drStep("workflowstepid")
            Dim stepResourceIdList As New List(Of String) '工位信息
            Dim stationlist As String = drStep("workstationids")
            For Each strStation As String In stationlist.Split(",")
                stepResourceIdList.Add(strStation)
            Next
            '占用
            Dim apsOccupyResDT = DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, stationlist.Replace(",", "','"), _beginDate.ToString("yyyy-MM-dd HH:mm:ss"), policylist))
            '负载均衡计算
            '负载均衡=多个工位时 按任务个数选择任务较少的工位 故过滤后剩下一个工位
            CalcLoadAVG(policylist, stationlist, stepResourceIdList)
            Dim resourceTable As New DataTable '工位设备列表 为兼容机加模拟加工方法而用
            resourceTable.Columns.Add("resourceid")
            resourceTable.Columns.Add("stepcycle")
            resourceTable.Columns.Add("stepcycle2")
            resourceTable.Columns.Add("qty")
            For Each strStation As String In stationlist.Split(",")
                resourceTable.Rows.Add(strStation, stepCycle, stepCyCle2, "1")
            Next

            Dim teamId As String = "" '组
            Dim shiftid As String = "" '班
            Dim workcenterid As String = "" '工区
            '此工序设备所有的班次信息
            Dim stepShiftDt = DatabaseHelper.Query_ReturnDatTable(String.Format(resWorkShift, stationlist.Replace(",", "','")))
            If stepShiftDt.Rows.Count > 0 Then
                workcenterid = stepShiftDt.Rows(0)("workcenterid").ToString
                shiftid = stepShiftDt.Rows(0)("shiftid").ToString
                If stepShiftDt.Select("workshiftid is not null").Length > 0 Then
                    stepShiftDt = stepShiftDt.Select("workshiftid is not null").CopyToDataTable()
                Else
                    stepShiftDt = stepShiftDt.Clone
                End If
            End If

            '设备模拟加工-工位无保养，厂级日历信息
            Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, faMakeCalendarDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)
            '到此此工序模拟加工完成，取出计算后的，工序的加工信息
            Dim dv As DataView = resSimulationDt.DefaultView
            dv.Sort = "simulateStart asc"
            Dim confirmResSL = resSimulationDt.Rows(0)

            Dim rowNo As Integer = dtStep.Rows.IndexOf(drStep)
            If (rowNo < dtStep.Rows.Count - 1) Then
                If (drStep("parallelgroupname").ToString <> dtStep.Rows(rowNo + 1)("parallelgroupname").ToString) Or (drStep("parallelgroupname").ToString = "") Then
                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                End If
            End If

            '检查工作日历
            curTime = curTime.AddDays(GetDateOffset(1, curTime, faMakeCalendarDt, 0))

            '生成工序计划sql
            Dim stepApsRows = stepApsDt.Select("workflowstepid='" + drStep("workflowstepid").ToString + "'")
            Dim stepApsId = Guid.NewGuid.ToString()
            If stepApsRows.Length > 0 Then
                stepApsId = stepApsRows(0)("Apsplandetailid")
                stepPlanSqls.Add(String.Format(stepUpdateSql, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), workcenterid, teamId,
                                          confirmResSL("resourceid"), para("FactoryID"), shiftid, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("resourceid"), conApsID, drStep("workflowstepid").ToString))

            Else
                stepPlanSqls.Add(String.Format(stepInsertSql, stepApsId, conApsID, containerid, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                         drStep("workflowstepid").ToString, drStep("workflowstepname").ToString, "", workcenterid, teamId,
                                          confirmResSL("resourceid"), para("EmployeeID"), para("FactoryID"), shiftid, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("resourceid")))

            End If
        Next

        '执行存入工序
        DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

        If apsplaninfoid <> "0" Then
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(stepSelectSql2, conApsID))

            '生成批次计划sql
            stepPlanSqls.Clear()
            If conApsDt.Rows.Count > 0 Then
                stepPlanSqls.Add(String.Format(conUpdateSql, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), containersApsId, conApsID))
            Else
                stepPlanSqls.Add(String.Format(conInsertSql, conApsID, policylist, containerid, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                               workflowid, containersApsId))
            End If
            '执行存入批次
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
        Else
            apsplaninfoid = conApsID
        End If
    End Sub

    ''' <summary>
    ''' 倒排
    ''' </summary>
    ''' <param name="dtStep"></param>
    ''' <param name="_beginDate"></param>
    ''' <param name="policylist"></param>
    ''' <param name="containerid"></param>
    ''' <param name="workflowid"></param>
    ''' <param name="para"></param>
    ''' <remarks></remarks>
    Sub ReverseCalculate(ByVal dtStep As DataTable, ByVal _beginDate As Date, ByVal policylist As String,
                          ByVal containerid As String, ByVal workflowid As String, ByVal para As Dictionary(Of String, String), Optional ByRef apsplaninfoid As String = "")

        '批次级排产信息sql
        Dim conSelectSql = <string> select * from ApsPlanInfo ai where ai.containerid='{0}' and ai.workflowid='{1}' and ai.ApsType2=2 and ai.ApsType='{2}' and ai.Isuse=0 </string>.Value
        Dim conInsertSql = <STIRNG> insert into ApsPlanInfo ai (ai.ApsType2,ai.apsplaninfoid,ai.apstype,ai.containerid,ai.createdate,ai.createemployeeid,ai.endtime,ai.isuse,
 ai.lastchangedate,ai.notes,ai.starttime,ai.workflowid,apsid) values (2,'{0}','{1}','{2}',sysdate,'{3}',to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
 SYSDATE,'{5}',to_date('{6}','YYYY-MM-DD HH24:MI:SS'),'{7}','{8}') </STIRNG>.Value
        Dim conUpdateSql = <string>update ApsPlanInfo ai set ai.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS') , ai.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'), 
                                    ai.lastchangedate=sysdate,apsid='{2}' where ai.apsplaninfoid='{3}'</string>.Value


        '工序级排产信息sql
        Dim stepSelectSql = <string>select * from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.ContainerID='{1}' and ad.isuse=0 </string>.Value
        Dim stepInsertSql = <string> insert into ApsPlanDetail ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
                                ad.workflowstepid,ad.workflowstepname,ad.notes,ad.workcenterid,ad.teamid,ad.workstationid,CreateEmployeeID,ad.SpecFactoryID,shiftid,ad.Resstarttime,ad.Resendtime,resourceid) 
                                values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
                                '{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}',to_date('{14}','YYYY-MM-DD HH24:MI:SS'),to_date('{15}','YYYY-MM-DD HH24:MI:SS'),'{16}') </string>.Value
        Dim stepUpdateSql = <string>    update ApsPlanDetail ad set  AD.Status=0,ad.FeedbackDate=null,ad.ConfirmDate=null,
                                        ad.ConfirmEmpID=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.workcenterid='{2}',ad.teamid='{3}',ad.workstationid='{4}',ad.SpecFactoryID='{5}',
                                        ad.shiftid='{6}',ad.Resstarttime=to_date('{7}','YYYY-MM-DD HH24:MI:SS'),ad.Resendtime=to_date('{8}','YYYY-MM-DD HH24:MI:SS'),
                                        ad.resourceid='{9}' 
                                        where ad.apsplaninfoid='{10}' and ad.workflowstepid='{11}' </string>.Value

        '通过工序级排产信息，计算批次的开始，结束时间
        Dim stepSelectSql2 = <string>select ad.containerid,min(ad.starttime) minstarttime,max(ad.endtime) maxendtime from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.isuse=0 group by ad.containerid </string>.Value

        '设备班次信息 总体工位和班关联
        Dim resWorkShift = <string> select t.shiftid, ws.workstationid resourceid,wf.workshiftid,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence 
                                    from workstation ws
                                    left join shift t on t.shiftid=ws.shiftid   
                                    left join workshift wf on wf.workshiftid=t.workshiftid
                                    left join workshiftdetail wd on wd.workshiftid=wf.workshiftid
                                    where ws.workstationid in ('{0}') and wf.isuse=0
                                    order by wf.workshiftid, wd.sequence asc </string>.Value

        '设备占用信息,当前结束时间之前,查询这些设备被排产后占用的情况，结果集需要排除已完工的
        Dim resOccupyAps As String =
            "select ad.* from ApsPlanDetail ad left join enginepartplan ep on ep.concontainerid=ad.containerid where ad.workstationid in ('{0}') and ad.isuse=0 and ad.Status in (1,2) " +
            "and ( ad.Resendtime<= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
            "or ( " +
            "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
            ") ) and ep.usecompleted='0' "
        '因本次排产未下发的批次间工位占用迭代执行，故此处并上本次排产的未下发部分yuxj20230301
        resOccupyAps = resOccupyAps + " union all " +
            " select apt.* from apsplandetail apt left join apsplaninfo ap on apt.apsplaninfoid=ap.apsplaninfoid where ap.apstype='{2}' and nvl(apt.status,'0')='0' and apt.isuse=0 " +
             "and apt.workstationid in ('{0}') "
        '+
        '            "and ( apt.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '            "or ( " +
        '            "apt.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and apt.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '            ") )"
        Dim endTime = _beginDate '倒排时赋值为结束时间
        Dim curTime = endTime

        If endTime = endTime.Date Then
            endTime = endTime.AddMinutes(1439) '初期结束时间为23:59:00
        End If

        Dim stepCycle As Single = 0 '工序加工周期，单位分钟
        Dim stepCycle2 As Single = 0 '设备加工周期，单位分钟
        Dim stepPlanSqls As New ArrayList
        Dim conApsID = Guid.NewGuid.ToString

        '外面传入的ApsPlanInfoID
        If apsplaninfoid <> "" And apsplaninfoid <> "0" Then
            conApsID = apsplaninfoid
        End If
        Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(conSelectSql, containerid, workflowid, policylist)) '批次的计划信息
        If conApsDt.Rows.Count > 0 Then
            conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
        End If

        Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(stepSelectSql, conApsID, containerid)) '批次工序的计划信息

        Dim stepResUpkeepDt As New DataTable
        stepResUpkeepDt.Columns.Add("resourceid")
        stepResUpkeepDt.Columns.Add("upkeepstartdate")
        stepResUpkeepDt.Columns.Add("upkeependdate")

        '倒排
        Dim dvStep As DataView = dtStep.DefaultView
        dvStep.Sort = "sequence desc "
        dtStep = dvStep.ToTable

        For Each drStep As DataRow In dtStep.Rows
            '如果此工序已经下发，直接跳过,规则：下发只能从前往后下发
            Dim stepApsRow = stepApsDt.Select(String.Format(" workflowstepid='{0}' ", drStep("workflowstepid").ToString))
            If stepApsRow.Length > 0 Then
                If stepApsRow(0)("Status").ToString = "1" Then
                    curTime = Date.Parse(stepApsRow(0)("StartTime").ToString) '已下发工序的开始时间
                    curTime = curTime.AddDays(GetDateOffset(2, curTime, faMakeCalendarDt, 0)) '检查工作日历
                    Continue For
                End If
            End If

            stepCycle = Single.Parse(drStep("transfercycle").ToString) + Single.Parse(drStep("makecycle").ToString) +
                 Single.Parse(drStep("preparecycle").ToString)

            stepCycle2 = Single.Parse(drStep("makecycle").ToString) +
                 Single.Parse(drStep("preparecycle").ToString)

            Dim stepid As String = drStep("workflowstepid")
            '工位信息
            Dim stepResourceIdList As New List(Of String)
            Dim stationlist As String = drStep("workstationids")
            For Each strStation As String In stationlist.Split(",")
                stepResourceIdList.Add(strStation)
            Next

            Dim teamid As String = "" '组
            Dim shiftid As String = "" '班
            '此工序设备所有的班次信息
            Dim stepShiftDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(resWorkShift, stationlist.Replace(",", "','")))
            If stepShiftDt.Rows.Count > 0 Then
                shiftid = stepShiftDt.Rows(0)("shiftid").ToString
            End If

            '占用
            Dim apsOccupyResDT = DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, stationlist.Replace(",", "','"), _beginDate.ToString("yyyy-MM-dd HH:mm:ss"), policylist))

            '负载均衡计算
            CalcLoadAVG(policylist, stationlist, stepResourceIdList)
            Dim resourceTable As New DataTable '工位设备列表 为兼容机加模拟加工方法而用
            resourceTable.Columns.Add("resourceid")
            resourceTable.Columns.Add("stepcycle")
            resourceTable.Columns.Add("stepcycle2")
            resourceTable.Columns.Add("qty")
            For Each strStation As String In stationlist.Split(",")
                resourceTable.Rows.Add(strStation, stepCycle, stepCycle2, "1")
            Next

            curTime = curTime.AddDays(GetDateOffset(2, curTime, faMakeCalendarDt, 0)) '检查工作日历
            Dim _startTime = curTime '此工序的初期开始时间

            '设备模拟加工-工位无保养，无厂级日历信息
            Dim resSimulationDt As DataTable = LMDescSimulationByRes(resourceTable, faMakeCalendarDt, faMakeCalendarDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)
            '到此此工序模拟加工完成，取出计算后的，工序的加工信息
            Dim dv As DataView = resSimulationDt.DefaultView
            dv.Sort = "simulateEnd desc"
            Dim confirmResSL = resSimulationDt.Rows(0)

            '处理平行工序
            Dim rowNo As Integer = dtStep.Rows.IndexOf(drStep)
            If (rowNo < dtStep.Rows.Count - 1) Then
                If (drStep("parallelgroupname").ToString <> dtStep.Rows(rowNo + 1)("parallelgroupname").ToString) Or (drStep("parallelgroupname").ToString = "") Then
                    curTime = Date.Parse(confirmResSL("simulateStart"))
                End If
            End If


            '生成工序计划sql
            Dim stepApsRows = stepApsDt.Select("workflowstepid='" + drStep("workflowstepid").ToString + "'")
            Dim stepApsId = Guid.NewGuid.ToString()
            If stepApsRows.Length > 0 Then
                stepApsId = stepApsRows(0)("Apsplandetailid")
                stepPlanSqls.Add(String.Format(stepUpdateSql, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), "", teamid,
                                          confirmResSL("resourceid"), para("FactoryID"), shiftid, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("resourceid"), conApsID, drStep("workflowstepid").ToString))

            Else
                stepPlanSqls.Add(String.Format(stepInsertSql, stepApsId, conApsID, containerid, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                         drStep("workflowstepid").ToString, drStep("workflowstepname").ToString, "", "", teamid,
                                          confirmResSL("resourceid"), para("EmployeeID"), para("FactoryID"), shiftid, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("resourceid")))

            End If
        Next

        '执行存入工序
        DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

        If apsplaninfoid <> "0" Then
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(stepSelectSql2, conApsID))

            '生成批次计划sql
            stepPlanSqls.Clear()
            If conApsDt.Rows.Count > 0 Then
                stepPlanSqls.Add(String.Format(conUpdateSql, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), containersApsId, conApsID))
            Else
                stepPlanSqls.Add(String.Format(conInsertSql, conApsID, policylist, containerid, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                               workflowid, containersApsId))
            End If
            '执行存入批次
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
        Else
            apsplaninfoid = conApsID
        End If
    End Sub

    ''' <summary>
    ''' 获取设备的排产数据
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetResApsDataForGrant(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select fa.factoryid,c.containername,ad.workflowstepid,ad.workflowstepname,ad.workstationid resourceid,rd.workstationname RESNAME,to_char(ad.ResStartTime,'yyyy/mm/dd') stepstart ,to_char(ad.ResEndTime,'yyyy/mm/dd') stepend 
                            ,to_char(ad.ResStartTime,'hh24:mi:ss') stepstarthms,to_char(ad.ResEndTime,'hh24:mi:ss') stependhms,ad.ApsPlanDetailid from ApsPlanInfo ai 
                            left join containercurrentinfo cci on cci.containerid=ai.containerid
                            left join ApsPlanDetail ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
                            left join workstation rd on rd.workstationid=ad.workstationid
                            left join shift s on s.shiftid=rd.shiftid
                            left join factory fa on fa.factoryid=rd.factoryid
                            left join container c on c.containerid=ai.containerid
                            where 1=1 and ai.isuse=0 and ai.ApsType2=2 
                    </string>.Value

        If para.ContainsKey("ResourceIds") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceIds")) Then
                strSql += String.Format(" and rd.workstationid in ({0}) ", para("ResourceIds"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and rd.factoryid='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("WorkcenterID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkcenterID")) Then
                strSql += String.Format(" and s.workcenterid='{0}' ", para("WorkcenterID"))
            End If
        End If

        If para.ContainsKey("TeamID") Then
            If Not String.IsNullOrWhiteSpace(para("TeamID")) Then
                strSql += String.Format(" and rd.TeamID='{0}' ", para("TeamID"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and nvl(ad.status,'0')='{0}' ", para("Status"))
            End If
        End If

        strSql += " order by ad.workstationid,ad.starttime "
        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '查询涉及分厂所有日历，设备所有日历
        Dim factoryIds = "", resourceIds = ""
        Dim faDt = dt.DefaultView.ToTable(True, "factoryid")
        Dim resDt = dt.DefaultView.ToTable(True, "resourceid")
        For Each row In faDt.Rows
            factoryIds += "'" + row("factoryid").ToString + "',"
        Next
        For Each row In resDt.Rows
            resourceIds += "'" + row("resourceid").ToString + "',"
        Next
        factoryIds = factoryIds.Trim(",")
        resourceIds = resourceIds.Trim(",")

        strSql = <string>select fa.factoryid,md.* from factory fa 
left join makecalendardetail md on md.makecalendarid=fa.makecalendarid
where fa.factoryid in ({0}) and md.makecalendarid is not null </string>.Value
        If String.IsNullOrWhiteSpace(factoryIds) Then
            factoryIds = "''"
        End If
        Dim faMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, factoryIds)) '分厂日历

        Dim resMKCDt As DataTable = New DataTable() '设备日历--总体没有设备日历

        '二次处理，将日期分割为一天一天
        Dim result = dt.Clone
        For Each row As DataRow In dt.Rows
            Dim orgStepStart = Date.Parse(row("stepstart").ToString)
            row("stepstart") = Date.Parse(row("stepstart"))
            Dim diffDays = (Date.Parse(row("stepend").ToString).Date - Date.Parse(row("stepstart").ToString).Date).Days
            If diffDays > 0 Then

                Dim orgStepEndHms = row("stependhms").ToString

                For i As Integer = 0 To diffDays
                    Dim reRow As DataRow = result.NewRow
                    reRow("resourceid") = row("resourceid")
                    reRow("RESNAME") = row("RESNAME")
                    reRow("ApsPlanDetailid") = row("ApsPlanDetailid")
                    reRow("containername") = row("containername")
                    reRow("workflowstepid") = row("workflowstepid")
                    reRow("workflowstepname") = row("workflowstepname")
                    If i = 0 Then
                        reRow("stepstarthms") = row("stepstarthms")
                    Else
                        reRow("stepstarthms") = "00:00:00"
                    End If
                    reRow("stepstart") = orgStepStart.AddDays(i)

                    reRow("stepend") = orgStepStart.AddDays(1 + i).AddMinutes(-1)


                    If i < diffDays Then
                        reRow("stependhms") = "23:59:00"
                    Else
                        reRow("stependhms") = orgStepEndHms
                    End If

                    reRow("stepstarthms") = Date.Parse(reRow("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stepstarthms").ToString
                    reRow("stependhms") = Date.Parse(reRow("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stependhms").ToString
                    '判断是否是假期 
                    If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    result.Rows.Add(reRow)
                Next

            Else
                row("stepend") = Date.Parse(row("stepstart").ToString).AddDays(1).AddMinutes(-1)

                row("stepstarthms") = Date.Parse(row("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stepstarthms").ToString
                row("stependhms") = Date.Parse(row("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stependhms").ToString
                '判断是否是假期
                If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                result.ImportRow(row)
            End If


        Next
        result.AcceptChanges()

        Dim temp = result.AsEnumerable()
        Dim temp2 = temp.OrderBy(Function(r) Date.Parse(r("stepstarthms"))) '转化日期后排序

        'yuxj20220826本方法有下发排产计划时才计算，处理查询结果为空时报错bug
        If result.Rows.Count > 0 Then
            result = temp2.CopyToDataTable
        End If


        Return result

    End Function

#Region "从机加类移动过来的方法yuxj2023-4-1"
    ''' <summary>
    ''' 移入批次到排产池
    ''' </summary>
    ''' <param name="containers"></param>
    ''' <remarks></remarks>
    Public Sub MoveContainerToAps(ByVal containers As DataTable)
        Dim sqlList As New ArrayList
        Dim sql = "update containercurrentinfo cci set cci.isinpool=1,cci.expectmaterialdate=to_date('{0}','yyyy/mm/dd') where cci.containerid='{1}' "
        For Each row As DataRow In containers.Rows
            sqlList.Add(String.Format(sql, row("expectmaterialdate").ToString, row("containerid").ToString))
        Next
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
    End Sub

    ''' <summary>
    ''' 获取批次的排产信息
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetContainerLMAps(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select cci.mfgordername,cci.containername,cci.productdescription productdesc,cci.qty,wb.workflowname||':'||w.workflowrevision workflowinfo,ai.apstype,
TO_CHAR(ai.starttime,'YYYY-MM-DD HH24:MI:SS') starttime,TO_CHAR(ai.endtime,'YYYY-MM-DD HH24:MI:SS') endtime,w.workflowid,cci.containerid,cci.containertype,cci.productid,ai.apsplaninfoid,
TO_CHAR(ep.plannedstartdate,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(ep.plannedfinishdate,'YYYY-MM-DD') MFGENDDDATE,to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate, 
TO_CHAR(ep.plannedstartdate,'YYYY-MM-DD') REALSTARTDATE,cci.workflowstepname,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,
DECODE(nvl(ai.status,0),0,'待调整确认',1,'已确认下发',2,'待审核确认','未知') status,--add by longb 2023-11-16
cci.productname,cci.productrevision,wb.workflowname,w.workflowrevision,cci.togetherflag  from ApsPlanInfo ai
left join containercurrentinfo cci on cci.containerid=ai.containerid
left join enginepartplan ep 
       on ep.concontainerid=ai.containerid
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
left join workflow w on w.workflowid=ai.workflowid
left join workflowbase wb on wb.workflowbaseid=w.workflowbaseid
where ai.apstype2=2  and ai.isuse=0 </string>.Value

        If para.ContainsKey("ApsType") Then
            If Not String.IsNullOrWhiteSpace(para("ApsType")) Then
                strSql += String.Format(" and ai.apstype='{0}' ", para("ApsType"))
            End If
        End If

        Dim condition As String = ""
        For Each r In conDt.Rows
            condition += " (1=1 "
            If conDt.Columns.Contains("containerid") And conDt.Columns.Contains("workflowid") Then
                condition += " and ai.containerid='" + r("containerid").ToString() + "'"
                condition += " and ai.workflowid='" + r("workflowid").ToString() + "'"
            End If
            If conDt.Columns.Contains("ApsType") Then
                condition += " and ai.ApsType=" + r("ApsType").ToString()
            End If
            condition += ") or"
        Next
        If condition <> "" Then
            condition = condition.Trim("r", "o")
            strSql += " and ( " + condition + " ) "
        End If

        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '批次的实际开始时间REALSTARTDATE：到料时间>计划开始时间=到料时间,到料时间为空：预计到料或齐套时间>计划开始时间=预计到料或齐套时间
        '如果已经在制了，开始时间则是当前时间
        For Each row As DataRow In dt.Rows
            If String.IsNullOrWhiteSpace(row("MATERIALDATE").ToString) Then
                If Not String.IsNullOrWhiteSpace(row("ExpectMaterialDate").ToString) Then '预计到料或齐套时间
                    If Date.Parse(row("ExpectMaterialDate").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                        row("REALSTARTDATE") = row("ExpectMaterialDate")
                    End If
                End If
            Else
                If Date.Parse(row("MATERIALDATE").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                    row("REALSTARTDATE") = row("MATERIALDATE")
                End If
            End If
        Next
        dt.AcceptChanges()
        Return dt
    End Function

    ''' <summary>
    ''' 移除排产池
    ''' </summary>
    ''' <param name="containers"></param>
    ''' <remarks></remarks>
    Public Sub RemoveApsPool(ByVal containers As DataTable)
        Dim sqlList As New ArrayList
        Dim sql = "update containercurrentinfo cci set cci.isinpool=0 where cci.containerid='{0}' "
        For Each row As DataRow In containers.Rows
            sqlList.Add(String.Format(sql, row("containerid").ToString))
        Next
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
    End Sub

    ''' <summary>
    ''' 调整无限产能排产---总体好像没什么用，先屏蔽 yuxj2023-4-1
    ''' </summary>
    ''' <param name="conAps"></param>
    ''' <remarks></remarks>
    Public Sub AdjustInfiniteCalculate(ByVal conAps As DataTable, ByVal para As Dictionary(Of String, String))
        'Dim positiveDt As New DataTable
        'If conAps.Select("apsType=1").Length > 0 Then
        '    positiveDt = conAps.Select("apsType=1").CopyToDataTable()
        'End If
        'Dim reverseDt As New DataTable
        'If conAps.Select("apsType=2").Length > 0 Then
        '    reverseDt = conAps.Select("apsType=2").CopyToDataTable
        'End If
        'PositiveCalculate(positiveDt, para)
        'ReverseCalculate(reverseDt, para)
    End Sub

    ''' <summary>
    ''' 正排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>     
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMAscSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date
                                   ) As DataTable


        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间
        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCyCle2 As Single = 0 '设备加工周期(去除了转运时间)
        For Each dr As DataRow In resourceTable.Rows
            Dim reourceId As String = dr("resourceid").ToString
            stepCycle = Single.Parse(dr("stepCycle").ToString)
            stepCyCle2 = Single.Parse(dr("stepCyCle2").ToString)

            Dim simulateStart = startTime '模拟开始时间
            Dim simulateEnd = endTime '模拟结束时间

            Dim simulateStart2 = startTime '设备模拟开始时间
            Dim simulateEnd2 = endTime '设备模拟结束时间

            Dim resultRow As DataRow = result.NewRow
            resultRow("resourceid") = reourceId

            '+设备本身日历，设备上有日历用设备的日历，无则用工厂日历
            Dim _stepResMKDt = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
            End If

            '取出此设备的班次加工信息
            Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
            Dim _stepShiftDt = stepShiftDt.Clone
            Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
            If _stepShiftDrs.Length > 0 Then
                _stepShiftDt = _stepShiftDrs.CopyToDataTable
                resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
            End If


            '----计算设备的工作周期(分钟)
            Dim resCycle = 1439
            If _stepShiftDt.Rows.Count > 1 Then
                resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
            End If

            '此设备的保养计划
            Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")
            Dim _stepResUpkeepDt As New DataTable
            If resUpKeepRows.Length > 0 Then
                _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
            End If

            '此设备占用数据
            Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
            Dim _apsOccupyResDT As New DataTable
            If resApsOccupyRows.Length > 0 Then
                _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
            End If

            '计算工序模拟加工的开始时间
            LMAscSLResStartDate(reourceId, simulateStart, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
            simulateEnd = simulateStart
            '计算工序模拟加工的结束时间
            LMAscSLResEndDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

            resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

            '计算设备模拟加工的开始时间
            'LMAscSLResStartDate(reourceId, simulateStart2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
            simulateStart2 = simulateStart
            simulateEnd2 = simulateStart2
            '计算设备模拟加工的结束时间
            LMAscSLResEndDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCyCle2, True)

            resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("qty") = dr("qty").ToString
            result.Rows.Add(resultRow)
        Next

        result.AcceptChanges()

        Return result

    End Function


    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，EndDate:结束时间
    ''' </summary>
    ''' <param name="simulateStart"></param>
    ''' <param name="simulateEnd"></param>
    ''' <param name="stepResMKDt"></param>
    ''' <param name="resUpKeepDt"></param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle"></param>
    ''' <param name="stepCycle"></param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMAscSLResEndDate(ByVal resourceId As String, ByVal simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                          ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                          ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)

        simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(1, simulateEnd, stepResMKDt, 1)
                    simulateEnd = simulateEnd.AddDays(offset + 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resRunCycle2, _stepShiftDt, stepResMKDt)
        Else '递归进来的情况
            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateEnd, resShiftStart) '设备开始时间

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resEnd = resEnd.AddDays(1)
            End If

            If resEnd < simulateEnd Then '例如：工序18：30结束，而设备默认17：30结束，追加一天
                Dim differ = GetDifferMins(simulateEnd, resEnd)

                simulateEnd = resEnd
                simulateEnd = simulateEnd.AddDays(1)
                simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))

                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt)
            ElseIf simulateEnd < resStart Then '结束时间，在设备未开始，eg:工序7:30结束，而设备8：30开始----->实际结束：8：30+1h
                Dim differ = GetDifferMins(resStart, simulateEnd)
                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt)
            Else '正常情况，结束时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '{``````[````}`````] 情况下才会只让 结束时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        'Dim removeRowIndex As New List(Of Integer) '需要移除的行
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1
            Dim resUpKeepRow = resUpKeepDt.Rows(i)

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If upkeepStart >= simulateStart And upkeepStart <= simulateEnd Then
                If upkeepEnd <= simulateEnd Then '{`````[````]````}
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf upkeepEnd > simulateEnd Then '{``````[````}`````]
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resUpKeepOffset2 += GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If

        Next

        simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resUpKeepOffset2, _stepShiftDt, stepResMKDt)
        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？

        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1
            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If apsStartTime >= simulateStart And apsStartTime <= simulateEnd Then
                If apsEndTime <= simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf apsEndTime > simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resApsOccupyOffset2 += GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If
        Next
        simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resApsOccupyOffset2, _stepShiftDt, stepResMKDt)
        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            LMAscSLResEndDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, False)
        End If

    End Sub

    ''' <summary>
    ''' 设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="simulateStart">设备模拟加工开始时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle">设备运转周期(分钟)</param>
    ''' <remarks></remarks>
    Sub LMAscSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String, ByVal resCycle As Single)

        '计算设备模拟加工开始时间

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0)) '检查工作日历,开始时间顺移

        '班次因素
        Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备初期开始时间
        Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备初期结束时间

        'Dim differ = GetDifferMins(simulateStart, resStart)

        If simulateStart <= resStart Then '工序开始7：30 ，设备默认开始8：30
            simulateStart = resStart
        ElseIf resStart < simulateStart And simulateStart < resEnd Then '工序开始时间在设备周期内,工序开始：9：30，而设备默认开始8：30,此时要考虑休息时间段如果9:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateStart = GetResApsStartOrEndDate(1, simulateStart, _stepShiftDt)
            End If

        Else '此情况例子，工序开始时间：18：30，而设备结束时间17：30，设备开始时间就要追加一天,跨天考虑日历情况
            simulateStart = resStart
            simulateStart = simulateStart.AddDays(1)

        End If
        '日历检查
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0))

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '[`````{```]````} 或者 '[`````{````}```]情况下才会让 开始时间 偏移


        Dim resUpKeepOffset1 = 0 '保养检查后，需要偏移的分钟数，开始时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟开始时间在保养周期内才会偏移
            If Not (simulateStart >= upkeepStart And simulateStart < upkeepEnd) Then
                Continue For
            End If

            resUpKeepOffset1 += GetDifferMins(upkeepEnd, simulateStart)
        Next

        simulateStart = simulateStart.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 = 0 '排产占用后，需要偏移的分钟数 
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            '模拟开始时间在占用周期内才会偏移
            If Not (simulateStart >= apsStartTime And simulateStart < apsEndTime) Then
                Continue For
            End If

            resApsOccupyOffset1 += GetDifferMins(apsEndTime, simulateStart)

        Next

        simulateStart = simulateStart.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            LMAscSLResStartDate(resourceId, simulateStart, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        End If

    End Sub


    ''' <summary>
    ''' 获取两个时间相差的分钟总数，向上取整
    ''' </summary>
    ''' <param name="endDate"></param>
    ''' <param name="startDate"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetDifferMins(ByVal endDate As Date, ByVal startDate As Date) As Single
        Dim re = (endDate - startDate).TotalMinutes
        Return Single.Parse(Math.Round(re))
    End Function

    ''' <summary>
    ''' 根据传入时间和班次，获取真正开始或结束时间
    ''' eg:2022-5-20 08:30:00
    ''' </summary>
    ''' <param name="d"></param>
    ''' <param name="workshift">格式例子：08:30:00</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetRunStartOrEndDate(ByVal d As Date, ByVal workshift As String) As Date
        Dim result = d.Date
        result = result.AddHours(Val(workshift.Split(":")(0)))
        result = result.AddMinutes(Val(workshift.Split(":")(1)))
        result = result.AddSeconds(Val(workshift.Split(":")(2)))

        Return result
    End Function

    ''' <summary>
    ''' 当时间在班次的最早开始和最晚结束时间内时，可用此方法
    ''' 正排的开始，倒排的结束可用
    ''' </summary>
    ''' <param name="type">1正排 2倒排</param>
    ''' <param name="d"></param>
    ''' <param name="stepShiftDt">班次详细列表</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsStartOrEndDate(ByVal type As Integer, ByVal d As Date, ByVal stepShiftDt As DataTable) As Date
        If stepShiftDt.Rows.Count = 0 Then
            Return d
        End If

        Dim _stepShiftDt = stepShiftDt.Copy

        Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

        For Each stepShiftRow In _stepShiftDt.Rows
            Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If

            If d >= startdatedetail And d <= enddatedetail Then '在有效加工时间内
                Return d
            End If

            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")

        Next
        _stepShiftDt.AcceptChanges()

        If type = 1 Then '正排
            '到此说明不在有效加工时间内
            If d < resStart Then
                d = resStart
            ElseIf d > resEnd Then
                d = resStart.AddDays(1)
            Else
                If _stepShiftDt.Rows.Count >= 2 Then
                    For i As Integer = 1 To _stepShiftDt.Rows.Count - 1
                        If d > Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail")) And d < Date.Parse(_stepShiftDt.Rows(i)("startdatedetail")) Then '判断在哪个休息时间段
                            d = Date.Parse(_stepShiftDt.Rows(i)("startdatedetail"))
                        End If
                    Next
                End If
            End If

        ElseIf type = 2 Then '倒排
            '到此说明不在有效加工时间内
            If d < resStart Then
                d = resEnd.AddDays(-1)
            ElseIf d > resEnd Then
                d = resEnd
            Else
                If _stepShiftDt.Rows.Count >= 2 Then
                    For i As Integer = 1 To _stepShiftDt.Rows.Count - 1
                        If d > Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail")) And d < Date.Parse(_stepShiftDt.Rows(i)("startdatedetail")) Then '判断在哪个休息时间段
                            d = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail"))
                        End If
                    Next
                End If
            End If
        Else
            Return d
        End If

        Return d

    End Function

    ''' <summary>
    ''' 当时间需要累加分钟数时，可用此方法 eg:班次时间段为 8:30-12:00,13:00-16:00,16:30-17:30三个时间段,考虑了班次跨天的情况
    ''' 正排的结束时间，倒排的开始时间可用
    ''' </summary>
    ''' <param name="type"></param>
    ''' <param name="d"></param>
    ''' <param name="addMinites">追加分钟数</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsEndOrStartDate(ByVal type As Integer, ByVal d As Date, ByVal addMinites As Single, ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable) As Date
        Dim orgD = d
        If stepShiftDt.Rows.Count = 0 Then
            If type = 1 Then
                d = d.AddMinutes(addMinites)
            ElseIf type = 2 Then
                d = d.AddMinutes(-addMinites)
            End If
        End If

        If stepShiftDt.Rows.Count > 0 Then
            Dim _stepShiftDt = stepShiftDt.Copy
            Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
            Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

            If type = 1 Then '正排
                While (addMinites > 0)

                    For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
                        Dim stepShiftRow = _stepShiftDt.Rows(i)

                        Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
                        Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

                        If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                            enddatedetail = enddatedetail.AddDays(1)
                        End If

                        Dim _differ = 0
                        If d >= startdatedetail And d <= enddatedetail Then '在此有效加工时间内
                            _differ = GetDifferMins(enddatedetail, d)
                            If addMinites > _differ Then
                                If i < _stepShiftDt.Rows.Count - 1 Then '下一个时间段的开始时间
                                    d = GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString)
                                Else '此时循环到最后一个时间段，第二天的最早开始时间
                                    d = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString).AddDays(1)
                                End If
                                addMinites = addMinites - _differ
                            Else
                                d = d.AddMinutes(addMinites)
                                addMinites = 0
                                Exit For
                            End If
                        Else

                        End If

                        If i < _stepShiftDt.Rows.Count - 1 Then
                            If d > enddatedetail And d < GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString) Then '异常，防止无限循环
                                addMinites = 0
                            End If
                        End If

                    Next
                End While
                '考虑日历
                If stepResMKDt.Rows.Count > 0 Then
                    Dim offset = 0 '偏移量
                    For i As Integer = 1 To (d.Date - orgD.Date).TotalDays
                        offset = GetDateOffset(1, orgD.AddDays(i), stepResMKDt, 0)
                        d = d.AddDays(offset)
                    Next
                End If
            ElseIf type = 2 Then '倒排
                While (addMinites > 0)

                    For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                        Dim stepShiftRow = _stepShiftDt.Rows(i)

                        Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
                        Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

                        If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                            startdatedetail = startdatedetail.AddDays(-1)
                        End If

                        Dim _differ = 0
                        If (d >= startdatedetail And d <= enddatedetail) Or
                            (d > GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString) And d < GetRunStartOrEndDate(d.AddDays(1), stepShiftRow("enddatedetail").ToString)) Then '在此有效加工时间内
                            _differ = GetDifferMins(d, startdatedetail)
                            If addMinites > _differ Then

                                If i > 0 Then '下一个时间段的开始时间
                                    d = GetRunStartOrEndDate(d, stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                                Else '此时循环到第一个时间段，前一天的最后结束时间
                                    d = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString).AddDays(-1)
                                End If
                                addMinites = addMinites - _differ
                            Else
                                d = d.AddMinutes(-addMinites)
                                addMinites = 0
                                Exit For
                            End If
                        Else

                        End If

                        If i > 0 Then
                            If d < startdatedetail And d > GetRunStartOrEndDate(d, stepShiftDt.Rows(i - 1)("enddatedetail").ToString) Then '异常，防止无限循环
                                addMinites = 0
                            End If
                        End If
                    Next
                End While
                '考虑日历
                If stepResMKDt.Rows.Count > 0 Then
                    Dim offset = 0 '偏移量
                    For i As Integer = 1 To (orgD.Date - d.Date).TotalDays
                        offset = GetDateOffset(2, orgD.AddDays(-i), stepResMKDt, 0)
                        d = d.AddDays(offset)
                    Next
                End If
            End If
        End If

        Return d
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetActualOccupyMins2(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable) As Single
        Dim _occupyStart = occupyStart, _occupyEnd = occupyEnd
        Dim re = 0
        '计算实际占用分钟数，考虑班次，休息时间
        Dim _days = (occupyEnd.Date - occupyStart.Date).TotalDays
        For j As Integer = 0 To _days
            _occupyStart = occupyStart.Date.AddDays(j)
            _occupyEnd = occupyStart.Date.AddDays(j + 1).AddMinutes(-1)

            '如果此占用的日期正在放假，不计算
            If stepResMKDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = _occupyStart.Date).AsDataView.Count > 0 Then
                Continue For
            End If

            If j = 0 Then
                _occupyStart = occupyStart
            End If
            If j = _days Then
                _occupyEnd = occupyEnd
            End If

            re += GetActualOccupyMins(_occupyStart, _occupyEnd, resShiftStart, resShiftEnd, stepShiftDt)

        Next
        Return re
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数,占用开始和结束在同天的情况
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetActualOccupyMins(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable) As Single
        If stepShiftDt.Rows.Count = 0 Then
            Return GetDifferMins(occupyEnd, occupyStart)
        End If
        Dim occupyMins As Single = 0 '实际占用的分钟数
        Dim occupyRestMins As Single = 0 '占用的休息时间
        Dim resStart = GetRunStartOrEndDate(occupyStart, resShiftStart) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(occupyStart, resShiftEnd) '设备最晚结束时间

        Dim _stepShiftDt = stepShiftDt.Copy()
        For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
            Dim stepShiftRow = _stepShiftDt.Rows(i)
            Dim startdatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If
            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")
        Next
        _stepShiftDt.AcceptChanges()
        '注释说明{：设备最早开始时间，}：设备最晚结束时间。[：占用开始时间,]:占用结束时间

        If Not (occupyStart < resStart And occupyEnd > resEnd) Then '此种情况下产生实际占用
            If occupyEnd > resEnd Then '[````{```````````}````] 或者 '{``````[````}`````] 情况下才会占用有效时间段
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                    End If
                Next
            Else '[````{```````````]````} 或者 '{``````[```````]``} 情况
                '取有效结束时间
                Dim occupyEndTemp = occupyEnd
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        Exit For
                    Else
                        If i > 0 Then
                            If occupyEndTemp >= Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString) Then
                                occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                            End If
                        End If
                    End If
                Next
                '计算有效占用时间
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If Not (occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString)) Then
                        Continue For
                    End If

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(occupyEndTemp, occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(occupyEndTemp, Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                        If i > 0 Then
                            occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                        End If
                    End If
                Next
            End If

        End If


        Return occupyMins
    End Function

    ''' <summary>
    ''' 倒排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>  
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMDescSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date) As DataTable

        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间

        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCycle2 As Single = 0 '增加设备加工周期
        For Each dr As DataRow In resourceTable.Rows
            Dim reourceId As String = dr("resourceid").ToString
            stepCycle = Single.Parse(dr("stepCycle").ToString)
            stepCycle2 = Single.Parse(dr("stepCyCle2").ToString)

            Dim simulateStart = startTime '模拟开始时间
            Dim simulateEnd = endTime '模拟结束时间

            Dim simulateStart2 = startTime '设备模拟开始时间
            Dim simulateEnd2 = endTime '设备模拟结束时间

            Dim resultRow As DataRow = result.NewRow
            resultRow("resourceid") = reourceId

            '+设备本身日历
            Dim _stepResMKDt = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
            End If

            Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
            Dim _stepShiftDt = stepShiftDt.Clone
            Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
            If _stepShiftDrs.Length > 0 Then
                _stepShiftDt = _stepShiftDrs.CopyToDataTable
                resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
            End If

            '----计算设备的工作周期(分钟)
            Dim resCycle = 1439
            If _stepShiftDt.Rows.Count > 1 Then
                resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
            End If

            '此设备的保养计划
            Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")

            Dim _stepResUpkeepDt As New DataTable
            If resUpKeepRows.Length > 0 Then
                _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
            End If

            '此设备占用数据
            Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
            Dim _apsOccupyResDT As New DataTable
            If resApsOccupyRows.Length > 0 Then
                _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
            End If


            '计算工序模拟加工结束时间
            LMDescSLResEndDate(reourceId, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)

            simulateStart = simulateEnd

            '计算工序模拟加工开始时间
            LMDescSLResStartDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

            resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

            '计算设备模拟加工的结束时间,往前算去除转运时间stepCycle-stepCycle2
            simulateEnd2 = simulateEnd
            simulateStart2 = simulateEnd
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle - stepCycle2, True)
            simulateEnd2 = simulateStart2

            '计算设备模拟加工的开始时间
            simulateStart2 = simulateEnd2
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle2, True)

            resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("qty") = dr("qty").ToString

            result.Rows.Add(resultRow)
        Next

        result.AcceptChanges()

        Return result

    End Function

    ''' <summary>
    ''' 倒排设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateStart">模拟开始时间</param>
    ''' <param name="simulateEnd">模拟结束时间</param>
    ''' <param name="stepResMKDt">设备日历</param>
    ''' <param name="resUpKeepDt">设备保养</param>
    ''' <param name="_apsOccupyResDT">设备占用</param>
    ''' <param name="_stepShiftDt">设备班次</param>
    ''' <param name="resShiftStart">设备班次最早开始时间</param>
    ''' <param name="resShiftEnd">设备班次最晚结束时间</param>
    ''' <param name="resCycle">设备运转周期（排除休息时间）</param>
    ''' <param name="stepCycle">工序(设备)需要加工周期</param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMDescSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                             ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                             ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(2, simulateStart, stepResMKDt, 1)
                    simulateStart = simulateStart.AddDays(offset - 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateStart = GetResApsEndOrStartDate(2, simulateStart, resRunCycle2, _stepShiftDt, stepResMKDt)
        Else '递归进来的情况
            simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备开始时间

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resStart = resStart.AddDays(-1)
            End If

            If simulateStart < resStart Then '例如：工序7:30开始，而设备8：30才开始
                Dim differ = GetDifferMins(simulateStart, resStart)

                simulateStart = resEnd
                simulateStart = simulateStart.AddDays(-1)
                simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt)
            ElseIf simulateStart > resEnd Then '例如：工序19：30开始，而设备18：30就已经结束
                Dim differ = GetDifferMins(resEnd, simulateStart)
                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt)
            Else '正常情况，开始时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateStart = GetResApsStartOrEndDate(2, simulateStart, _stepShiftDt)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '[````{`````]````}  情况下才会只让 开始时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1

            Dim resUpKeepRow = resUpKeepDt.Rows(i)
            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If simulateEnd > upkeepEnd And simulateStart < upkeepEnd Then

                If simulateStart > upkeepStart Then '[````{`````]````}                   
                    ' resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf simulateStart < upkeepStart Then '{`````[````]````} 
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                End If

                '计算实际占用分钟数，考虑班次，休息时间
                resUpKeepOffset2 += GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If

        Next
        simulateStart = GetResApsEndOrStartDate(2, simulateStart, resUpKeepOffset2, _stepShiftDt, stepResMKDt)

        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？ 
        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是计算出占用设备的实际分钟数
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1

            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If simulateEnd > apsEndTime And simulateStart < apsEndTime Then
                If simulateStart > apsStartTime Then '[````{`````]````}
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf simulateStart < apsStartTime Then '{`````[````]````} 
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resApsOccupyOffset2 += GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
            End If
        Next
        simulateStart = GetResApsEndOrStartDate(2, simulateStart, resApsOccupyOffset2, _stepShiftDt, stepResMKDt)

        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            LMDescSLResStartDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd,
                                 resCycle, stepCycle, False)
        End If

    End Sub

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，EndDate:结束时间 
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateEnd">设备模拟加工结束时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次详细信息eg:8:30-12:00,13:00-15:00,17:30-19:30</param>
    ''' <param name="resShiftStart">设备班次最早时间eg:8:30</param>
    ''' <param name="resShiftEnd">设备班次最晚时间eg:19:30</param>
    ''' <param name="resCycle">设备实际运转周期(去除休息时间)</param>
    ''' <remarks></remarks>
    Sub LMDescSLResEndDate(ByVal resourceId As String, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                            ByVal resCycle As Integer)

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0)) '检查工作日历,结束时间向前顺移

        '班次因素
        Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备初期结束时间      

        Dim differ = GetDifferMins(resEnd, simulateEnd)

        If differ < 0 Then '工序结束19：30 ，设备默认结束18：30
            simulateEnd = resEnd
        ElseIf differ <= resCycle Then '工序结束时间在设备周期内,工序结束：17：30，而设备结束18：30,,此时要考虑休息时间段如果17:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt)
            End If

        Else '此情况例子，工序结束时间：6：30，而设备开始时间8：30.设备结束时间18：30，真正结束时间就要向前推一天，跨天考虑日历

            simulateEnd = resEnd
            simulateEnd = simulateEnd.AddDays(-1)

            'simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0))
            ''减去剩余的分钟数
            'simulateEnd = simulateEnd.AddMinutes(resCycle - differ)

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````}````]或者 '[`````{````}```]情况下才会让 结束时间向前偏移

        Dim resUpKeepOffset1 As Single = 0 '保养检查后，需要偏移的分钟数，开始时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟结束时间在保养周期内才会偏移
            If Not (simulateEnd >= upkeepStart And simulateEnd < upkeepEnd) Then
                Continue For
            End If

            resUpKeepOffset1 -= GetDifferMins(simulateEnd, upkeepStart)
        Next

        simulateEnd = simulateEnd.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况

        Dim resApsOccupyOffset1 As Single = 0 '排产占用后，需要偏移的分钟数 
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            '模拟结束时间在占用周期内才会偏移
            If Not (simulateEnd >= apsStartTime And simulateEnd < apsEndTime) Then
                Continue For
            End If

            resApsOccupyOffset1 -= GetDifferMins(simulateEnd, apsStartTime)

        Next

        simulateEnd = simulateEnd.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            LMDescSLResEndDate(resourceId, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        End If

    End Sub

#End Region

End Class
